Skip to content

Storage and bindings

A Worker reaches storage through bindings on env, such as env.DB.prepare(…) for a D1 database. There are two ways to get one, and a Worker can use both.

Resources in the site’s own config belong to that site. The build makes each one the first time it’s declared, binds it under the name you give, keeps it across builds by its key, and deletes it once the config stops declaring it. D1 migrations in the repository are applied on every build. The site config reference has every field.

Use this for storage that is part of the site’s code: its database and the schema that goes with it, its cache, its upload bucket.

Sites › D1 databases, R2 buckets, KV namespaces and Queues hold the workspace’s own storage, which isn’t tied to any one site. A database there can be queried from the dashboard, a bucket’s files browsed, a queue paused or purged.

To use one from a Worker, add it on the Worker’s Bindings tab under a variable name, or from the API with POST /api/v1/sites/{site}/bindings:

POST /api/v1/sites/shop/bindings
{ "resource": "orders-archive", "kind": "d1", "variable": "ARCHIVE" }

resource is the resource’s name or id, and kind is d1, r2, kv, queue_producer or queue_consumer. A binding reaches a live Worker straight away when it can (applied: true), else at its next deploy. Removing it (DELETE /api/v1/sites/{site}/bindings/{binding}) leaves the resource and its data as they are.

The storage itself is in the API too, under /api/v1/storage/d1, /storage/r2, /storage/kv and /storage/queues, such as POST /api/v1/storage/d1 and POST /api/v1/storage/d1/{database}/query.

A site’s config can only produce to its queues. To take messages off a queue, make the queue in Sites › Queues and add the Worker as its consumer on the Worker’s Bindings tab, with kind: "queue_consumer" from the API. The Worker has to be deployed first, and it needs a queue handler:

src/index.ts
interface Env {
DB: D1Database;
}
export default {
async queue(batch: MessageBatch<{ orderId: string }>, env: Env): Promise<void> {
for (const message of batch.messages) {
await env.DB.prepare("UPDATE orders SET exported = 1 WHERE id = ?")
.bind(message.body.orderId)
.run();
message.ack();
}
},
};

The consumer’s batch size, wait, retries and concurrency, and the queue’s dead-letter queue, are set on the queue’s page or with PATCH /api/v1/storage/queues/{queue}.

Durable Objects are declared in the config, with the classes the Worker exports: durable_objects and migrations.

A Worker that sends email gets a send_email binding from Sites › Email sending, limited to the sending domain’s addresses, at its next deploy. send_email in wrangler.jsonc is skipped.