Skip to content

Commit a94f883

Browse files
authored
Adds documentation for importable env (#20509)
Adds documentation for importable env
1 parent 5708808 commit a94f883

File tree

2 files changed

+224
-17
lines changed

2 files changed

+224
-17
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
title: Import `env` to access bindings in your Worker's global scope
3+
description: More easily configure your Worker and call bindings from anywhere with an importable `env`
4+
products:
5+
- workers
6+
date: 2025-03-17T15: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 do not allow I/O from outside a request context. This means that even
38+
though `env` is accessible from the top-level scope, you will not be able to access
39+
every binding's methods.
40+
41+
For instance, environment variables and secrets are accessible, and you are able to
42+
call `env.NAMESPACE.get` to get a [Durable Object stub](/durable-objects/api/stub/) in the
43+
top-level context. However, calling methods on the Durable Object stub, making [calls to a KV store](/kv/api/),
44+
and [calling to other Workers](/workers/runtime-apis/bindings/service-bindings) will not work.
45+
:::
46+
47+
Additionally, `env` was normally accessed as a argument to a Worker's entrypoint handler,
48+
such as [`fetch`](/workers/runtime-apis/fetch/).
49+
This meant that if you needed to access a binding from a deeply nested function,
50+
you had to pass `env` as an argument through many functions to get it to the
51+
right spot. This could be cumbersome in complex codebases.
52+
53+
Now, you can access the bindings from anywhere in your codebase
54+
without passing `env` as an argument:
55+
56+
```js
57+
// helpers.js
58+
import { env } from "cloudflare:workers";
59+
60+
// env is *not* an argument to this function
61+
export async function getValue(key) {
62+
let prefix = env.KV_PREFIX;
63+
return await env.KV.get(`${prefix}-${key}`);
64+
}
65+
```
66+
67+
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: 157 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,153 @@ 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` as a global
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+
in top-level global scope. For example, to initialize an API client:
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 from outside a request context. This means that even
131+
though `env` is accessible from the top-level scope, you will not be able to access
132+
every binding's methods.
133+
134+
For instance, environment variables and secrets are accessible, and you are able to
135+
call `env.NAMESPACE.get` to get a [Durable Object stub](/durable-objects/api/stub/) in the
136+
top-level context. However, calling methods on the Durable Object stub, making [calls to a KV store](/kv/api/),
137+
and [calling to other Workers](/workers/runtime-apis/bindings/service-bindings) will not work.
138+
139+
```js
140+
import { env } from "cloudflare:workers";
141+
142+
// This would error!
143+
// env.KV.get('my-key')
144+
145+
export default {
146+
async fetch(req) {
147+
// This works
148+
let myVal = await env.KV.get("my-key");
149+
Response.new(myVal);
150+
},
151+
};
152+
```
153+
154+
Additionally, importing `env` from `cloudflare:workers` lets you avoid passing `env`
155+
as an argument through many function calls if you need to access a binding from a deeply-nested
156+
function. This can be helpful in a complex codebase.
157+
158+
```js
159+
import { env } from "cloudflare:workers";
160+
161+
export default {
162+
fetch(req) {
163+
Response.new(sayHello());
164+
},
165+
};
166+
167+
// env is not an argument to sayHello...
168+
function sayHello() {
169+
let myName = getName();
170+
return `Hello, ${myName}`;
171+
}
172+
173+
// ...nor is it an argument to getName
174+
function getName() {
175+
return env.MY_NAME;
73176
}
74177
```
75178

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.
179+
:::note
180+
While using `env` from `cloudflare:workers` may be simpler to write than passing it
181+
through a series of function calls, passing `env` as an argument is a helpful pattern
182+
for dependency injection and testing.
183+
:::
184+
185+
### Overriding `env` values
186+
187+
The `withEnv` function provides a mechanism for overriding values of `env`.
188+
189+
Imagine a user has defined the [environment variable](/workers/configuration/environment-variables/)
190+
"NAME" to be "Alice" in their Wrangler configuration file and deployed a Worker. By default, logging
191+
`env.NAME` would print "Alice". Using the `withEnv` function, you can override the value of
192+
"NAME".
193+
194+
```js
195+
import { env, withEnv } from "cloudflare:workers";
196+
197+
function logName() {
198+
console.log(env.NAME);
199+
}
200+
201+
export default {
202+
fetch(req) {
203+
// this will log "Alice"
204+
logName();
205+
206+
withEnv({ NAME: "Bob" }, () => {
207+
// this will log "Bob"
208+
logName();
209+
});
210+
211+
// ...etc...
212+
},
213+
};
214+
```
215+
216+
This can be useful when testing code that relies on an imported `env` object.

0 commit comments

Comments
 (0)