Skip to content

Commit 4ba9919

Browse files
committed
Adds documentation for importable env
1 parent 448aa16 commit 4ba9919

File tree

2 files changed

+214
-17
lines changed

2 files changed

+214
-17
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
title: Import Env for direct access to bindings
3+
description: More easily configure your worker and call bindings from anywhere in your Worker with an importable `env`
4+
products:
5+
- workers
6+
date: 2025-03-04T11:00:00Z
7+
---
8+
9+
import { Render, TypeScriptExample } from "~/components";
10+
11+
You can now access [bindings](/workers/runtime-apis/bindings/)
12+
from anywhere in your Worker by importing the `env` object from `cloudflare:workers`.
13+
14+
Previously, `env` could only be accessed during a request. This meant that
15+
bindings could not be used in the top-level context of a Worker.
16+
17+
Now, you can import `env` and access bindings such as [secrets](/workers/configuration/secrets/)
18+
or [environment variables](/workers/configuration/environment-variables/) in the
19+
initial setup for your Worker:
20+
21+
```js
22+
import { env } from "cloudflare:workers";
23+
import ApiClient from "example-api-client";
24+
25+
// API_KEY and LOG_LEVEL now usable in top-level scope
26+
const apiClient = ApiClient.new({ apiKey: env.API_KEY });
27+
const LOG_LEVEL = env.LOG_LEVEL || "info";
28+
29+
export default {
30+
fetch(req) {
31+
// you can use apiClient or LOG_LEVEL, configured before any request is handled
32+
},
33+
};
34+
```
35+
36+
:::note
37+
Workers error if attempting to perform I/O from outside a request context.
38+
This means that even though `env` is accessible from the top-level scope, not
39+
all bindings will work.
40+
:::
41+
42+
Additionally, `env` was normally accessed as a argument to a Worker's entrypoint handler,
43+
such as [`fetch`](/workers/runtime-apis/fetch/).
44+
This meant that if you needed to access a binding from a deeply nested function,
45+
you had to pass `env` as an argument through many functions to get it to the
46+
right spot. This could be cumbersome in complex codebases.
47+
48+
Now, you can access the bindings from anywhere in your codebase
49+
without passing `env` as an argument:
50+
51+
```js
52+
// helpers.js
53+
import { env } from "cloudflare:workers";
54+
55+
// env is *not* an argument to this function
56+
export async function getValue(key) {
57+
let prefix = env.KV_PREFIX;
58+
return await env.KV.get(`${prefix}-${key}`);
59+
}
60+
```
61+
62+
For more information, see [documentation on accessing `env`](/workers/runtime-apis/bindings#how-to-access-env).

src/content/docs/workers/runtime-apis/bindings/index.mdx

Lines changed: 152 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ pcx_content_type: concept
33
title: Bindings (env)
44
head: []
55
description: Worker Bindings that allow for interaction with other Cloudflare Resources.
6-
76
---
87

9-
import { DirectoryListing, WranglerConfig } from "~/components"
8+
import { DirectoryListing, WranglerConfig } from "~/components";
109

1110
Bindings allow your Worker to interact with resources on the Cloudflare Developer Platform. Bindings provide better performance and less restrictions when accessing resources from Workers than the [REST APIs](/api/) which are intended for non-Workers applications.
1211

@@ -31,12 +30,12 @@ r2_buckets = [
3130

3231
```js
3332
export default {
34-
async fetch(request, env) {
35-
const key = url.pathname.slice(1);
36-
await env.MY_BUCKET.put(key, request.body);
37-
return new Response(`Put ${key} successfully!`);
38-
}
39-
}
33+
async fetch(request, env) {
34+
const key = url.pathname.slice(1);
35+
await env.MY_BUCKET.put(key, request.body);
36+
return new Response(`Put ${key} successfully!`);
37+
},
38+
};
4039
```
4140

4241
You can think of a binding as a permission and an API in one piece. With bindings, you never have to add secret keys or tokens to your Worker in order to access resources on your Cloudflare account — the permission is embedded within the API itself. The underlying secret is never exposed to your Worker's code, and therefore can't be accidentally leaked.
@@ -51,12 +50,12 @@ The following is a good approach:
5150

5251
```ts
5352
export default {
54-
fetch(request, env) {
55-
let client = new Client(env.MY_SECRET); // `client` is guaranteed to be up-to-date with the latest value of `env.MY_SECRET` since a new instance is constructed with every incoming request
53+
fetch(request, env) {
54+
let client = new Client(env.MY_SECRET); // `client` is guaranteed to be up-to-date with the latest value of `env.MY_SECRET` since a new instance is constructed with every incoming request
5655

57-
// ... do things with `client`
58-
}
59-
}
56+
// ... do things with `client`
57+
},
58+
};
6059
```
6160

6261
Compared to this alternative, which might have surprising and unwanted behavior:
@@ -65,12 +64,148 @@ Compared to this alternative, which might have surprising and unwanted behavior:
6564
let client = undefined;
6665

6766
export default {
68-
fetch(request, env) {
69-
client ??= new Client(env.MY_SECRET); // `client` here might not be updated when `env.MY_SECRET` changes, since it may already exist in global scope
67+
fetch(request, env) {
68+
client ??= new Client(env.MY_SECRET); // `client` here might not be updated when `env.MY_SECRET` changes, since it may already exist in global scope
69+
70+
// ... do things with `client`
71+
},
72+
};
73+
```
74+
75+
If you have more advanced needs, explore the [AsyncLocalStorage API](/workers/runtime-apis/nodejs/asynclocalstorage/), which provides a mechanism for exposing values down to child execution handlers.
76+
77+
## How to access `env`
78+
79+
Bindings are located on the `env` object, which can be accessed in several ways:
80+
81+
- It is an argument to entrypoint handlers such as [`fetch`](/workers/runtime-apis/fetch/):
82+
83+
```js
84+
export default {
85+
async fetch(request, env) {
86+
return new Response(`Hi, ${env.NAME}`);
87+
},
88+
};
89+
```
7090

71-
// ... do things with `client`
91+
* It is as class property on [WorkerEntrypoint](/workers/runtime-apis/bindings/service-bindings/rpc/#bindings-env),
92+
[DurableObject](/durable-objects/), and [Workflow](/workflows/):
93+
94+
```js
95+
export class MyDurableObject extends DurableObject {
96+
async sayHello() {
97+
return `Hi, ${this.env.NAME}!`;
98+
}
7299
}
100+
```
101+
102+
* It can be imported from `cloudflare:workers`:
103+
104+
```js
105+
import { env } from "cloudflare:workers";
106+
console.log(`Hi, ${this.env.Name}`);
107+
```
108+
109+
### Importing `env`
110+
111+
Importing `env` from `cloudflare:workers` is useful when you need to access a binding
112+
such as [secrets](/workers/configuration/secrets/) or [environment variables](/workers/configuration/environment-variables/)
113+
to do initial Worker configuration.
114+
115+
```js
116+
import { env } from "cloudflare:workers";
117+
import ApiClient from "example-api-client";
118+
119+
// API_KEY and LOG_LEVEL now usable in top-level scope
120+
let apiClient = ApiClient.new({ apiKey: env.API_KEY });
121+
const LOG_LEVEL = env.LOG_LEVEL || "info";
122+
123+
export default {
124+
fetch(req) {
125+
// you can use apiClient or LOG_LEVEL, configured before any request is handled
126+
},
127+
};
128+
```
129+
130+
Workers do not allow I/O outside of a request context. This means that certain bindings will
131+
not work from the top-level context. For instance, making a call to the [KV binding](/kv/concepts/kv-bindings/)
132+
will error, but making it from within a request will work.
133+
134+
```js
135+
import { env } from "cloudflare:workers";
136+
137+
// This would error!
138+
// env.KV.get('my-key')
139+
140+
export default {
141+
async fetch(req) {
142+
// This works
143+
let myVal = await env.KV.get("my-key");
144+
Response.new(myVal);
145+
},
146+
};
147+
```
148+
149+
Additionally, importing `env` from `cloudflare:workers` lets you avoid passing `env`
150+
as an argument through many function calls if you need to access a binding from a deeply-nested
151+
function. This can be helpful in a complex codebase.
152+
153+
```js
154+
import { env } from "cloudflare:workers";
155+
156+
export default {
157+
fetch(req) {
158+
Response.new(sayHello());
159+
},
160+
};
161+
162+
// env is not an argument to sayHello...
163+
function sayHello() {
164+
let myName = getName();
165+
return `Hello, ${myName}`;
166+
}
167+
168+
// ...nor is it an argument to getName
169+
function getName() {
170+
return env.MY_NAME;
73171
}
74172
```
75173

76-
If you have more advanced needs, explore the [AsyncLocalStorage API](/workers/runtime-apis/nodejs/asynclocalstorage/), which provides a mechanism for exposing values down to child execution handlers.
174+
:::note
175+
While using `env` from `cloudflare:workers` may be simpler to write than passing it
176+
through a series of function calls, passing `env` as an argument is a helpful pattern
177+
for dependency injection and testing.
178+
:::
179+
180+
### Overriding `env` values
181+
182+
The `withEnv` function provides a mechanism for overriding values of `env`.
183+
184+
Imagine a user has defined the [environment variable](/workers/configuration/environment-variables/)
185+
"NAME" to be "Alice" in their Wrangler configuration file and deployed a worker. By default, logging
186+
`env.NAME` would print "Alice". Using the `withEnv` function, you can override the value of
187+
"NAME".
188+
189+
```js
190+
import { env, withEnv } from "cloudflare:workers";
191+
192+
function logName() {
193+
console.log(env.NAME);
194+
}
195+
196+
export default {
197+
fetch(req) {
198+
// this will log "Alice"
199+
logName();
200+
201+
withEnv({ NAME: "Bob" }, () => {
202+
// this will log "Bob"
203+
logName();
204+
});
205+
206+
// ...etc...
207+
},
208+
};
209+
```
210+
211+
This can be useful when testing code that relies on an imported `env` object.

0 commit comments

Comments
 (0)