You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 205e1cf
Browse filesBrowse the repository at this point in the historyBrowse files
description: Route resource operations through a platform-owned Worker to control per-tenant access.
4
+
description: Intercept User Worker binding calls to log usage, enforce limits, and run platform-owned logic before operations reach a Cloudflare resource.
5
5
sidebar:
6
6
order: 6
7
7
products:
@@ -17,18 +17,26 @@ import {
17
17
TabItem,
18
18
} from"~/components";
19
19
20
-
A custom binding routes a User Worker's resource operations through your Platform Worker before they reach the underlying resource. You wrap a Cloudflare resource (for example, a [D1](/d1/) database) inside a [`WorkerEntrypoint`](/workers/runtime-apis/bindings/service-bindings/rpc/#the-workerentrypoint-class) class and use [`props`](/workers/runtime-apis/context/#props) to pass the real resource connection to your Platform Worker at upload time.
20
+
Custom bindings let your platform intercept calls that User Workers make to Cloudflare resource bindings. Instead of calling the underlying resource directly, each operation first passes through logic in a Platform Worker that you control.
21
21
22
-
The User Worker calls the resource API as usual. It does not know that the binding routes through your Platform Worker first.
22
+
For example, when a User Worker calls `env.BUCKET.put()`, your Platform Worker could:
23
+
24
+
- Measure the size of the upload and record it for billing.
25
+
- Check whether the customer has exceeded their storage limit.
26
+
- Reject objects that do not meet your platform's requirements.
27
+
- Forward allowed uploads to the underlying R2 bucket.
28
+
29
+
From the User Worker's perspective, `BUCKET` still behaves like a normal R2 binding. The User Worker does not need to know about your interception logic or have direct access to the underlying resource.
23
30
24
31
## Use cases
25
32
26
-
Use this pattern when you deploy [User Workers](/cloudflare-for-platforms/workers-for-platforms/) and need to:
33
+
Use custom bindings when you need to run platform-owned logic around resource operations:
27
34
28
-
-**Enforce query policy** — inspect and filter operations before they reach the resource.
29
-
-**Scope access per tenant** — use `this.ctx.props` to select a tenant-specific resource or apply tenant-specific rules.
30
-
-**Log or meter usage** — record every operation for auditing or billing.
31
-
-**Rate-limit operations** — throttle operations per tenant or per plan tier.
35
+
-**Log or meter usage** — Record operations, bytes, or other usage data for auditing and billing.
36
+
-**Enforce limits** — Apply quotas or rate limits based on the customer or plan tier.
37
+
-**Add custom logic** — Validate, transform, or reject operations according to your platform's requirements.
38
+
-**Scope access per tenant** — Select the resource assigned to a customer and prevent access to other tenants' resources.
39
+
-**Enforce resource policy** — Restrict which operations a User Worker can perform before they reach the resource.
32
40
33
41
## Supported resources
34
42
@@ -37,59 +45,76 @@ Use this pattern when you deploy [User Workers](/cloudflare-for-platforms/worker
37
45
|[D1](/d1/)| Available |
38
46
|[R2](/r2/)| Not yet available |
39
47
40
-
## The pattern
48
+
The conceptual examples below use R2 because its `get()` and `put()` operations make the interception pattern easy to understand. The runnable implementation uses D1, which is currently the supported resource.
41
49
42
-
Every custom binding follows the same three-step structure, regardless of the underlying resource.
1. Define the platform-owned logic that handles intercepted resource operations.
55
+
2. Give that logic the underlying resource and tenant-specific context it needs.
56
+
3. Attach the custom binding so the User Worker can call the standard resource API.
47
57
48
-
```ts
49
-
import { WorkerEntrypoint } from "cloudflare:workers";
58
+
### Step 1: Define where intercepted calls are handled
50
59
51
-
export class MyCustomBinding extends WorkerEntrypoint {
52
-
async query(params) {
53
-
// Inspect params, then forward or reject.
54
-
return this.ctx.props.RESOURCE.query(params);
55
-
}
56
-
}
57
-
```
60
+
Your platform needs a trusted place to receive each resource operation before it reaches the underlying resource. Define this logic as methods on a [`WorkerEntrypoint`](/workers/runtime-apis/bindings/service-bindings/rpc/#the-workerentrypoint-class) exported by your Platform Worker.
61
+
62
+
The method that Cloudflare invokes depends on the resource. Using R2 as a conceptual example, an upload made with `env.BUCKET.put()` would be handled by a `put()` method in your Platform Worker.
63
+
64
+
Inside that method, you can run your own logic. For example, you could measure the upload, record usage, check the customer's storage quota, and either reject the operation or forward it to the real bucket.
The interception logic needs to know which resource and platform rules apply to the User Worker. Pass this trusted information through [`props`](/workers/runtime-apis/context/#props) when you configure the custom binding during the User Worker upload.
Using the R2 example, your Platform Worker could receive the customer's bucket, tenant ID, and storage limit through `props`. Its `put()` method could then apply that customer's limits before forwarding the upload.
### Step 3: Call the resource from the User Worker
84
+
85
+
The User Worker uses the standard API for the resource. It does not call your Platform Worker directly.
86
+
87
+
Using R2 as a conceptual example, the User Worker would call:
88
+
89
+
```js
90
+
awaitenv.BUCKET.put("image.png", image);
91
+
```
92
+
93
+
The `binding_interface` configuration makes the binding expose the resource's native API. Cloudflare routes the operation to the corresponding method in your Platform Worker, where your custom logic runs.
94
+
95
+
For D1, the User Worker continues to call methods such as `env.DB.prepare().bind().all()`. No changes are required to its database code.
D1 custom bindings use a `query()` method in the Platform Worker. Cloudflare translates the User Worker's standard D1 calls, such as `prepare()`, `bind()`, and `all()`, into a `query()` call.
100
+
101
+
Your `query()` method can inspect the statements, record usage, enforce limits, or reject the operation. If the operation is allowed, it forwards the request to the D1 database provided through `this.ctx.props`.
This procedure assumes that you already have a Workers for Platforms dispatch namespace and dynamic dispatch Worker. If you do not, complete the [Workers for Platforms get started guide](/cloudflare-for-platforms/workers-for-platforms/get-started/) first.
The User Worker calls `env.DB.prepare().bind().all()` as usual. Save this file as `worker.js` — the file name must match the `main_module` value used when you upload the User Worker.
User Workers are uploaded into a dispatch namespace and invoked through a [dynamic dispatch Worker](/cloudflare-for-platforms/workers-for-platforms/configuration/dynamic-dispatch/). They are not routable directly.
271
-
272
-
```sh
273
-
wranglerdispatch-namespacecreatecustom-binding-ns
274
-
```
275
-
276
-
#### 4. Upload the User Worker with the custom binding
293
+
#### 3. Upload the User Worker with the custom binding
277
294
278
295
Configure `env.DB` as a service binding to the Platform Worker, then add two fields that turn it into a custom D1 binding:
279
296
@@ -286,7 +303,7 @@ Upload with the [Workers for Platforms script upload API](/api/resources/workers
0 commit comments