Cloudflare Workers Setup Reference

Type: Software Reference Confidence: 0.93 Sources: 7 Verified: 2026-02-28 Freshness: 2026-02-28

TL;DR

Constraints

Quick Reference

SettingValueNotes
nameWorker nameRequired — used as subdomain
mainsrc/index.tsEntry point (ES modules default)
compatibility_date2026-02-28Required — controls runtime flags
compatibility_flags["nodejs_compat_v2"]Enables Node.js polyfills
[vars]key-value pairsNon-secret env vars
[[kv_namespaces]]binding, idKV namespace binding
[[r2_buckets]]binding, bucket_nameR2 object storage binding
[[d1_databases]]binding, database_idD1 SQLite database binding
[env.staging]Environment overridesDeploy with --env staging
routes[{pattern, zone_name}]Custom domain routing
workers_devtrue (default)Enables *.workers.dev subdomain
placement.modesmartRuns closer to backend
[build]command, cwdCustom build command

Decision Tree

START
├── Need key-value storage with global replication?
│   ├── YES → Use KV (eventually consistent, read-optimized)
│   └── NO ↓
├── Need relational queries (SQL)?
│   ├── YES → Use D1 (SQLite at the edge)
│   └── NO ↓
├── Need to store files/blobs (>25 MB)?
│   ├── YES → Use R2 (S3-compatible, no egress fees)
│   └── NO ↓
├── Need strong consistency / coordination?
│   ├── YES → Use Durable Objects (single-instance, stateful)
│   └── NO ↓
├── Need background processing?
│   ├── YES → Use Queues (at-least-once delivery)
│   └── NO ↓
└── DEFAULT → Plain Worker with fetch handler, no storage

Step-by-Step Guide

1. Create a new Workers project

Scaffold a project with Wrangler CLI. [src1]

npm create cloudflare@latest my-worker
cd my-worker

Verify: ls wrangler.toml src/index.ts → both files exist

2. Configure wrangler.toml

Set up bindings for KV, R2, D1. [src1] [src5]

name = "my-worker"
main = "src/index.ts"
compatibility_date = "2026-02-28"
compatibility_flags = ["nodejs_compat_v2"]

[vars]
API_BASE = "https://api.example.com"

[[kv_namespaces]]
binding = "CACHE"
id = "abc123def456"

[[d1_databases]]
binding = "DB"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

Verify: npx wrangler whoami → shows account

3. Write the Worker handler

Create a module Worker with typed bindings. [src4]

export interface Env {
  CACHE: KVNamespace;
  DB: D1Database;
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    const url = new URL(request.url);
    if (url.pathname === '/health') {
      return Response.json({ status: 'ok' });
    }
    return new Response('Not Found', { status: 404 });
  }
} satisfies ExportedHandler<Env>;

Verify: npx wrangler dev → starts at localhost:8787

4. Deploy to production

Deploy the Worker to Cloudflare's edge. [src2]

npx wrangler deploy

Verify: curl https://my-worker.your-account.workers.dev/health{"status":"ok"}

Code Examples

TypeScript: REST API with D1 and KV caching

// Input:  HTTP request to /api/posts/:id
// Output: JSON response with post data, cached in KV

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    const url = new URL(request.url);
    const match = url.pathname.match(/^\/api\/posts\/(\d+)$/);
    if (!match) return new Response('Not Found', { status: 404 });

    const cacheKey = `post:${match[1]}`;
    const cached = await env.CACHE.get(cacheKey, 'json');
    if (cached) return Response.json(cached);

    const post = await env.DB.prepare(
      'SELECT * FROM posts WHERE id = ?'
    ).bind(match[1]).first();

    if (!post) return Response.json({ error: 'Not found' }, { status: 404 });
    ctx.waitUntil(env.CACHE.put(cacheKey, JSON.stringify(post), { expirationTtl: 300 }));
    return Response.json(post);
  }
} satisfies ExportedHandler<Env>;

TypeScript: R2 file upload

// Input:  PUT /upload/:filename with binary body
// Output: Stores file in R2, returns metadata

export default {
  async fetch(request: Request, env: Env) {
    if (request.method === 'PUT' && new URL(request.url).pathname.startsWith('/upload/')) {
      const filename = new URL(request.url).pathname.replace('/upload/', '');
      const object = await env.UPLOADS.put(filename, request.body, {
        httpMetadata: { contentType: request.headers.get('content-type') || 'application/octet-stream' }
      });
      return Response.json({ key: object.key, size: object.size }, { status: 201 });
    }
    return new Response('Method Not Allowed', { status: 405 });
  }
} satisfies ExportedHandler<Env>;

Anti-Patterns

Wrong: Destructuring ctx (loses this binding)

// ❌ BAD — causes "Illegal invocation"
const { waitUntil } = ctx;
waitUntil(somePromise);

Correct: Call methods on ctx directly

// ✅ GOOD — keep ctx intact
ctx.waitUntil(somePromise);

Wrong: Using Service Worker syntax

// ❌ BAD — legacy format
addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request));
});

Correct: Use ES Module syntax

// ✅ GOOD — module format with typed env
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    return new Response('Hello');
  }
} satisfies ExportedHandler<Env>;

Wrong: Secrets in wrangler.toml

# ❌ BAD — visible in version control
[vars]
API_KEY = "sk-live-abc123"

Correct: Use wrangler secret

# ✅ GOOD — encrypted, never in code
npx wrangler secret put API_KEY

Wrong: Blocking response for background work

// ❌ BAD — user waits for analytics write
const data = await getData(env);
await env.DB.prepare('INSERT INTO analytics ...').run();
return Response.json(data);

Correct: Use ctx.waitUntil()

// ✅ GOOD — response returns immediately
const data = await getData(env);
ctx.waitUntil(env.DB.prepare('INSERT INTO analytics ...').run());
return Response.json(data);

Common Pitfalls

Diagnostic Commands

# Check Wrangler version and auth
npx wrangler --version && npx wrangler whoami

# Start local dev with real bindings
npx wrangler dev --remote

# Tail production logs
npx wrangler tail

# List deployments
npx wrangler deployments list

# Check KV contents
npx wrangler kv key list --namespace-id=YOUR_ID

# Run D1 query
npx wrangler d1 execute my-db --command "SELECT count(*) FROM items"

Version History & Compatibility

VersionStatusBreaking ChangesMigration Notes
Wrangler v3CurrentModule Workers default, new configMigrate from Service Worker syntax
Wrangler v2DeprecatedRun npx wrangler@3 init
nodejs_compat_v2CurrentReplaces nodejs_compatBetter polyfill coverage
D1GA (2024)Production-ready
R2GA (2023)S3-compatible API

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
Low-latency edge compute (<50 ms)Long-running tasks >30 s CPUAWS Lambda, Cloud Run
Global distribution (300+ PoPs)Heavy CPU (ML inference)GPU cloud functions
Simple KV or SQL storageLarge relational DB (>10 GB)Managed PostgreSQL
File storage with no egressPOSIX filesystem neededEC2, Cloud VMs
Cost-sensitive (<$5/mo)WebSocket >30 minDurable Objects

Important Caveats

Related Units