Skip to main content

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.
wrangler.json
{
  "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);
  }
}
binding
string
required
Variable name to access the namespace in your code
id
string
required
KV namespace ID (obtained from dashboard or CLI)
preview_id
string
Different namespace to use during local development with wrangler dev

D1 Databases

Serverless SQL database built on SQLite.
wrangler.json
{
  "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);
  }
}
binding
string
required
Variable name to access the database
database_id
string
D1 database UUID (required for deployment, can be omitted if database_name exists)
database_name
string
Database name for reference and auto-provisioning

R2 Buckets

Object storage compatible with S3 API.
wrangler.json
{
  "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);
  }
}
binding
string
required
Variable name to access the bucket
bucket_name
string
required
R2 bucket name (must be unique per account)
jurisdiction
string
Data jurisdiction for compliance (e.g., "eu" for GDPR)

Durable Objects

Stateful objects with persistent storage and guaranteed single-instance execution.
wrangler.json
{
  "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.
wrangler.json
{
  "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.
wrangler.json
{
  "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.
wrangler.json
{
  "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:
wrangler.json
{
  "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

wrangler.json
{
  "kv_namespaces": [
    { "binding": "DATA", "id": "..." }
  ],
  "r2_buckets": [
    { "binding": "DATA", "bucket_name": "..." }
  ]
}
❌ Error: Duplicate binding name DATA

Type Safety

Define TypeScript types for your bindings:
env.d.ts
interface Env {
  MY_KV: KVNamespace;
  DB: D1Database;
  MY_BUCKET: R2Bucket;
  API_KEY: string;
}

Multiple Environments

Different bindings for development, staging, and production:
wrangler.json
{
  "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.

Build docs developers (and LLMs) love