Overview
Bindings allow your Worker to interact with Cloudflare resources like KV namespaces, D1 databases, R2 buckets, Durable Objects, and other services. Bindings are configured in your wrangler.json and made available to your Worker at runtime.
Bindings are environment variables that provide access to resources. Theyβre type-safe in TypeScript and validated at deployment time.
Binding Types
KV Namespaces
Key-value storage for reading and writing data globally.
{
"kv_namespaces" : [
{
"binding" : "MY_KV" ,
"id" : "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
},
{
"binding" : "CACHE" ,
"id" : "preview_id_here" ,
"preview_id" : "different_preview_id"
}
]
}
Usage:
export default {
async fetch ( request , env ) {
// Read from KV
const value = await env . MY_KV . get ( "key" );
// Write to KV
await env . MY_KV . put ( "key" , "value" , {
expirationTtl: 3600
});
return new Response ( value );
}
}
Variable name to access the namespace in your code
KV namespace ID (obtained from dashboard or CLI)
Different namespace to use during local development with wrangler dev
D1 Databases
Serverless SQL database built on SQLite.
{
"d1_databases" : [
{
"binding" : "DB" ,
"database_name" : "production-db" ,
"database_id" : "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6"
}
]
}
Usage:
export default {
async fetch ( request , env ) {
// Query D1
const result = await env . DB . prepare (
"SELECT * FROM users WHERE id = ?"
). bind ( 123 ). first ();
return Response . json ( result );
}
}
Variable name to access the database
D1 database UUID (required for deployment, can be omitted if database_name exists)
Database name for reference and auto-provisioning
R2 Buckets
Object storage compatible with S3 API.
{
"r2_buckets" : [
{
"binding" : "MY_BUCKET" ,
"bucket_name" : "my-bucket" ,
"jurisdiction" : "eu"
}
]
}
Usage:
export default {
async fetch ( request , env ) {
// Upload to R2
await env . MY_BUCKET . put ( "file.txt" , "contents" );
// Download from R2
const object = await env . MY_BUCKET . get ( "file.txt" );
if ( object === null ) {
return new Response ( "Not found" , { status: 404 });
}
return new Response ( object . body );
}
}
Variable name to access the bucket
R2 bucket name (must be unique per account)
Data jurisdiction for compliance (e.g., "eu" for GDPR)
Durable Objects
Stateful objects with persistent storage and guaranteed single-instance execution.
{
"durable_objects" : {
"bindings" : [
{
"name" : "COUNTER" ,
"class_name" : "Counter" ,
"script_name" : "my-worker"
}
]
}
}
Usage:
export class Counter {
constructor ( private state : DurableObjectState ) {}
async fetch ( request : Request ) {
let count = ( await this . state . storage . get ( "count" )) || 0 ;
count ++ ;
await this . state . storage . put ( "count" , count );
return new Response ( count . toString ());
}
}
export default {
async fetch ( request , env ) {
const id = env . COUNTER . idFromName ( "global" );
const stub = env . COUNTER . get ( id );
return stub . fetch ( request );
}
}
Service Bindings
Call other Workers directly without HTTP overhead.
{
"services" : [
{
"binding" : "AUTH_SERVICE" ,
"service" : "auth-worker" ,
"environment" : "production"
}
]
}
Usage:
export default {
async fetch ( request , env ) {
// Call another Worker
const authResponse = await env . AUTH_SERVICE . fetch ( request );
if ( ! authResponse . ok ) {
return new Response ( "Unauthorized" , { status: 401 });
}
return new Response ( "Authenticated!" );
}
}
Queue Bindings
Send and consume messages from Cloudflare Queues.
{
"queues" : {
"producers" : [
{
"binding" : "MY_QUEUE" ,
"queue" : "my-queue-name"
}
],
"consumers" : [
{
"queue" : "my-queue-name" ,
"max_batch_size" : 10 ,
"max_batch_timeout" : 30
}
]
}
}
Producer:
export default {
async fetch ( request , env ) {
await env . MY_QUEUE . send ({
url: request . url ,
timestamp: Date . now ()
});
return new Response ( "Queued" );
}
}
Consumer:
export default {
async queue ( batch , env ) {
for ( const message of batch . messages ) {
console . log ( "Processing:" , message . body );
message . ack ();
}
}
}
Environment Variables
Simple string values for configuration.
{
"vars" : {
"API_URL" : "https://api.example.com" ,
"MAX_RETRY" : "3" ,
"FEATURE_FLAG" : "true"
}
}
Usage:
export default {
async fetch ( request , env ) {
const maxRetry = parseInt ( env . MAX_RETRY );
const response = await fetch ( env . API_URL );
return response ;
}
}
Secrets
Secure values that are encrypted and not visible in config.
# Set a secret
wrangler secret put API_KEY
# Enter the value when prompted
# List secrets
wrangler secret list
# Delete a secret
wrangler secret delete API_KEY
Usage:
export default {
async fetch ( request , env ) {
const response = await fetch ( "https://api.example.com" , {
headers: {
"Authorization" : `Bearer ${ env . API_KEY } `
}
});
return response ;
}
}
Never commit secrets to version control. Always use wrangler secret to manage sensitive values.
Auto-Provisioning
Wrangler can automatically create resources during deployment if they donβt exist.
Interactive Provisioning
During deployment, Wrangler prompts to create missing resources:
wrangler deploy
Provisioning MY_KV (KV Namespace )...
? Would you like to connect an existing KV Namespace or create a new one ?
βΊ Create new
my-existing-namespace (id: abc123... )
Enter a name for your new KV Namespace
βΊ my-worker-my-kv
π Creating new KV Namespace "my-worker-my-kv"...
β¨ MY_KV provisioned π
Auto-Create Mode
Use --auto-create to skip prompts:
wrangler deploy --auto-create
Resources are created with default names: {worker-name}-{binding-name}
Resource Inheritance
If a Worker is already deployed with bindings, Wrangler can inherit them:
{
"kv_namespaces" : [
{
"binding" : "MY_KV"
// ID omitted - will inherit from existing deployment
}
]
}
Inherited bindings use the same resource from the previous deployment. This is useful when restructuring config without changing resource IDs.
Binding Validation
Wrangler validates bindings at deployment time:
Name Conflicts
{
"kv_namespaces" : [
{ "binding" : "DATA" , "id" : "..." }
],
"r2_buckets" : [
{ "binding" : "DATA" , "bucket_name" : "..." }
]
}
β Error: Duplicate binding name DATA
Type Safety
Define TypeScript types for your bindings:
interface Env {
MY_KV : KVNamespace ;
DB : D1Database ;
MY_BUCKET : R2Bucket ;
API_KEY : string ;
}
Multiple Environments
Different bindings for development, staging, and production:
{
"name" : "my-worker" ,
"defined_environments" : [ "production" , "staging" ],
"kv_namespaces" : [
{ "binding" : "DATA" , "id" : "dev-namespace-id" }
],
"env" : {
"staging" : {
"kv_namespaces" : [
{ "binding" : "DATA" , "id" : "staging-namespace-id" }
]
},
"production" : {
"kv_namespaces" : [
{ "binding" : "DATA" , "id" : "prod-namespace-id" }
]
}
}
}
Deploy to specific environment:
wrangler deploy --env production
Viewing Bindings
Print all configured bindings:
wrangler deploy --dry-run
Output shows:
Bindings:
- KV Namespaces:
- MY_KV: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
- D1 Databases:
- DB: my-database (uuid: ...)
- R2 Buckets:
- MY_BUCKET: my-bucket
- Vars:
- API_URL: "https://api.example.com"
Best Practices
Use Descriptive Names Name bindings clearly: USER_KV, PRODUCT_DB, UPLOAD_BUCKET rather than generic names.
Separate Environments Use different resources for dev/staging/production to avoid data conflicts.
Type Your Env Always define TypeScript interfaces for your environment to catch errors early.
Document Bindings Add comments in config explaining what each binding is used for.