Skill: tenequm/cloudflare-workers (version 3.1.0)

--- name: cloudflare-workers description: Rapid development with Cloudflare Workers - build and deploy serverless applications on Cloudflare's global network. Use when building APIs, full-stack web apps, edge functions, background jobs, or real-time applications. Triggers on phrases like "cloudflare workers", "wrangler", "edge computing", "serverless cloudflare", "workers bindings", or files like wrangler.toml, worker.ts, worker.js. metadata: version: "3.1.0" openclaw: homepage: https://github.com/tenequm/skills/tree/main/skills/cloudflare-workers emoji: "☁️" primaryEnv: CLOUDFLARE_API_TOKEN requires: bins: - wrangler envVars: - name: CLOUDFLARE_API_TOKEN required: false description: Cloudflare API token (scoped). Required for wrangler deploy/upload. - name: ACCOUNT_ID required: false description: Cloudflare account ID. - name: API_TOKEN required: false description: Generic API token used in some examples. - name: PLATFORM_SECRET required: false description: Platform-level secret for dispatched workers. - name: LOG_TOKEN required: false description: Token for log sinks. - name: DATADOG_API_KEY required: false description: Datadog observability key. - name: HONEYCOMB_API_KEY required: false description: Honeycomb observability key. --- # Cloudflare Workers ## Overview Cloudflare Workers is a serverless execution environment that runs JavaScript, TypeScript, Python, and Rust code on Cloudflare's global network. Workers execute in milliseconds, scale automatically, and integrate with Cloudflare's storage and compute products through bindings. **Key Benefits:** - **Zero cold starts** - Workers run in V8 isolates, not containers - **Global deployment** - Code runs in 300+ cities worldwide - **Rich ecosystem** - Bindings to D1, KV, R2, Durable Objects, Queues, Containers, Workflows, and more - **Full-stack capable** - Build APIs and serve static assets in one project - **Standards-based** - Uses Web APIs (fetch, crypto, streams, WebSockets) ## When to Use This Skill Use Cloudflare Workers for: - **APIs and backends** - RESTful APIs, GraphQL, tRPC, WebSocket servers - **Full-stack applications** - React, Next.js, Remix, Astro, Vue, Svelte with static assets - **Edge middleware** - Authentication, rate limiting, A/B testing, routing - **Background processing** - Scheduled jobs (cron), queue consumers, webhooks - **Data transformation** - ETL pipelines, real-time data processing - **AI applications** - RAG systems, chatbots, image generation with Workers AI - **Durable workflows** - Multi-step long-running tasks with automatic retries (Workflows) - **Container workloads** - Run Docker containers alongside Workers (Containers) - **MCP servers** - Host remote Model Context Protocol servers - **Proxy and gateway** - API gateways, content transformation, protocol translation ## Quick Start Workflow ### 1. Install Wrangler CLI ```bash npm install -g wrangler # Login to Cloudflare wrangler login ``` ### 2. Create a New Worker ```bash # Using C3 (create-cloudflare) - recommended npm create cloudflare@latest my-worker # Or create manually wrangler init my-worker cd my-worker ``` ### 3. Write Your Worker **Basic HTTP API (TypeScript):** ```typescript export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url); if (url.pathname === "/api/hello") { return Response.json({ message: "Hello from Workers!" }); } return new Response("Not found", { status: 404 }); }, }; ``` **With environment variables and KV:** ```typescript interface Env { MY_VAR: string; MY_KV: KVNamespace; } export default { async fetch(request: Request, env: Env): Promise<Response> { // Access environment variable const greeting = env.MY_VAR; // Read from KV const value = await env.MY_KV.get("my-key"); return Response.json({ greeting, value }); }, }; ``` ### 4. Develop Locally ```bash # Start local development server with hot reload wrangler dev # Access at http://localhost:8787 ``` ### 5. Deploy to Production ```bash # Deploy to workers.dev subdomain wrangler deploy # Deploy to custom domain (configure in wrangler.toml) wrangler deploy ``` ## Core Concepts ### Workers Runtime Workers use the V8 JavaScript engine with Web Standard APIs: - **Execution model**: Isolates (not containers) - instant cold starts - **CPU time limit**: 10ms (Free), 30s (Paid) per request - **Memory limit**: 128 MB per isolate - **Languages**: JavaScript, TypeScript, Python, Rust - **APIs**: fetch, crypto, streams, WebSockets, WebAssembly **Supported APIs:** - Fetch API (HTTP requests) - URL API (URL parsing) - Web Crypto (encryption, hashing) - Streams API (data streaming) - WebSockets (real-time communication) - Cache API (edge caching) - HTML Rewriter (HTML transformation) ### Handlers Workers respond to events through handlers: **Fetch Handler** (HTTP requests): ```typescript export default { async fetch(request: Request, env: Env, ctx: ExecutionContext) { return new Response("Hello!"); }, }; ``` **Scheduled Handler** (cron jobs): ```typescript export default { async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext) { // Runs on schedule defined in wrangler.toml await env.MY_KV.put("last-run", new Date().toISOString()); }, }; ``` **Queue Handler** (message processing): ```typescript export default { async queue(batch: MessageBatch<any>, env: Env, ctx: ExecutionContext) { for (const message of batch.messages) { await processMessage(message.body); message.ack(); } }, }; ``` ### Bindings Bindings connect your Worker to Cloudflare resources. Configure in `wrangler.toml`: **KV (Key-Value Storage):** ```toml [[kv_namespaces]] binding = "MY_KV" id = "your-kv-namespace-id" ``` ```typescript // Usage await env.MY_KV.put("key", "value"); const value = await env.MY_KV.get("key"); ``` **D1 (SQL Database):** ```toml [[d1_databases]] binding = "DB" database_name = "my-database" database_id = "your-database-id" ``` ```typescript // Usage const result = await env.DB.prepare( "SELECT * FROM users WHERE id = ?" ).bind(userId).all(); ``` **R2 (Object Storage):** ```toml [[r2_buckets]] binding = "MY_BUCKET" bucket_name = "my-bucket" ``` ```typescript // Usage await env.MY_BUCKET.put("file.txt", "contents"); const object = await env.MY_BUCKET.get("file.txt"); const text = await object?.text(); ``` **Environment Variables:** ```toml [vars] [REDACTED_SECRET]" # pragma: allowlist secret ``` **Secrets** (sensitive data): ```bash # Set via CLI (not in wrangler.toml) wrangler secret put API_KEY ``` ### Context (ctx) The `ctx` parameter provides control over request lifecycle: ```typescript export default { async fetch(request: Request, env: Env, ctx: ExecutionContext) { // Run tasks after response is sent ctx.waitUntil( env.MY_KV.put("request-count", String(Date.now())) ); // Pass through to origin on exception ctx.passThroughOnException(); return new Response("OK"); }, }; ``` ### Top-level Environment Access Since March 2025, you can import `env` at the module level instead of passing it through handlers: ```typescript import { env } from "cloudflare:workers"; // Access bindings outside of handlers const apiClient = new ApiClient({ [REDACTED_SECRET] }); export default { async fetch(request: Request): Promise<Response> { // env is also available here without the parameter const data = await env.MY_KV.get("config"); return Response.json({ data }); }, }; ``` This eliminates prop-drilling `env` through function signatures and enables module-level initialization. ## Rapid Development Patterns ### Wrangler Configuration **Essential `wrangler.toml`:** ```toml name = "my-worker" main = "src/index.ts" compatibility_date = "2025-09-01" # Custom domain routes = [ { pattern = "api.example.com/*", zone_name = "example.com" } ] # Or workers.dev subdomain workers_dev = true # Environment variables [vars] ENVIRONMENT = "production" # Bindings [[kv_namespaces]] binding = "CACHE" id = "your-kv-id" [[d1_databases]] binding = "DB" database_name = "production-db" database_id = "your-db-id" [[r2_buckets]] binding = "ASSETS" bucket_name = "my-assets" # Cron triggers [triggers] crons = ["0 0 * * *"] # Daily at midnight ``` ### Environment Management Use environments for staging/production: ```toml [env.staging] vars = { ENVIRONMENT = "staging" } [env.staging.d1_databases] binding = "DB" database_name = "staging-db" database_id = "staging-db-id" [env.production] vars = { ENVIRONMENT = "production" } [env.production.d1_databases] binding = "DB" database_name = "production-db" database_id = "production-db-id" ``` ```bash # Deploy to staging wrangler deploy --env staging # Deploy to production wrangler deploy --env production ``` ### Common Patterns **JSON API with Error Handling:** ```typescript export default { async fetch(request: Request, env: Env): Promise<Response> { try { const url = new URL(request.url); if (url.pathname === "/api/users" && request.method === "GET") { const users = await env.DB.prepare("SELECT * FROM users").all(); return Response.json(users.results); } if (url.pathname === "/api/users" && request.method === "POST") { const body = await request.json(); await env.DB.prepare( "INSERT INTO users (name, email) VALUES (?, ?)" ).bind(body.name, body.email).run(); return Response.json({ success: true }, { status: 201 }); } return Response.json({ error: "Not found" }, { status: 404 }); } catch (error) { return Response.json( { error: error.message }, { status: 500 } ); } }, }; ``` **Authentication Middleware:** ```typescript async function authenticate(request: Request, env: Env): Promise<string | null> { const authHeader = request.headers.get("Authorization"); if (!authHeader?.startsWith("Bearer ")) { return null; } const [REDACTED_SECRET]); const userId = await env.SESSIONS.get(token); return userId; } export default { async fetch(request: Request, env: Env): Promise<Response> { const userId = await authenticate(request, env); if (!userId) { return Response.json({ error: "Unauthorized" }, { status: 401 }); } // Proceed with authenticated request return Response.json({ userId }); }, }; ``` **CORS Headers:** ```typescript const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization", }; export default { async fetch(request: Request): Promise<Response> { if (request.method === "OPTIONS") { return new Response(null, { headers: corsHeaders }); } const response = await handleRequest(request); // Add CORS headers to response Object.entries(corsHeaders).forEach(([key, value]) => { response.headers.set(key, value); }); return response; }, }; ``` ### Static Assets (Full-Stack Apps) Serve static files alongside your Worker code: ```toml [assets] directory = "./public" binding = "ASSETS" ``` ```typescript export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url); // API routes if (url.pathname.startsWith("/api/")) { return handleAPI(request, env); } // Serve static assets via the ASSETS binding return env.ASSETS.fetch(request); }, }; ``` ### Testing **Using Vitest:** ```typescript import { env, createExecutionContext } from "cloudflare:test"; import { describe, it, expect } from "vitest"; import worker from "./index"; describe("Worker", () => { it("responds with JSON", async () => { const request = new Request("http://example.com/api/hello"); const ctx = createExecutionContext(); const response = await worker.fetch(request, env, ctx); expect(response.status).toBe(200); expect(await response.json()).toEqual({ message: "Hello!" }); }); }); ``` ## Framework Integration Workers supports major frameworks with adapters: - **Next.js** - Full App Router and Pages Router support - **Remix / React Router** - Native Cloudflare adapter - **Astro** - Server-side rendering on Workers - **SvelteKit** - Cloudflare adapter available - **Hono** - Lightweight web framework built for Workers - **tRPC** - Type-safe APIs with full Workers support **Example with Hono:** ```typescript import { Hono } from "hono"; const app = new Hono(); app.get("/", (c) => c.text("Hello!")); app.get("/api/users/:id", async (c) => { const id = c.req.param("id"); const user = await c.env.DB.prepare( "SELECT * FROM users WHERE id = ?" ).bind(id).first(); return c.json(user); }); export default app; ``` ## Advanced Topics For detailed information on advanced features, see the reference files: - **Complete Bindings Guide**: `references/bindings-complete-guide.md` - All binding types (D1, KV, R2, Durable Objects, Queues, Workers AI, Vectorize, Workflows, Containers, Secrets Store, Pipelines, AutoRAG) - **Deployment & CI/CD**: `references/wrangler-and-deployment.md` - Wrangler v4 migration, commands, GitHub Actions, GitLab CI/CD, gradual rollouts, remote bindings - **Development Best Practices**: `references/development-patterns.md` - Testing, debugging, error handling, performance, top-level env access patterns - **Advanced Features**: `references/advanced-features.md` - Containers, Workflows, MCP servers, Workers for Platforms, WebSockets, Node.js compat, streaming - **Observability**: `references/observability.md` - Logging (tail, Logpush, Workers Logs), metrics, traces, debugging ## Resources **Official Documentation:** - Workers: https://developers.cloudflare.com/workers/ - Wrangler CLI: https://developers.cloudflare.com/workers/wrangler/ - Runtime APIs: https://developers.cloudflare.com/workers/runtime-apis/ - Examples: https://developers.cloudflare.com/workers/examples/ - Workflows: https://developers.cloudflare.com/workflows/ - Containers: https://developers.cloudflare.com/containers/ **Templates & Quick Starts:** - Templates: https://developers.cloudflare.com/workers/get-started/quickstarts/ - Framework guides: https://developers.cloudflare.com/workers/framework-guides/ **Community:** - Discord: https://discord.cloudflare.com - GitHub: https://github.com/cloudflare/workers-sdk

path: LICENSE.txt
content: Apache License
Version 2.0, January 2004
https://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.

"Licensor" shall mean the copyright owner or entity authorized by the
copyright owner that is granting the License.

"Legal Entity" shall mean the union of the acting entity and all other
entities that control, are controlled by, or are under common control with
that entity. For the purposes of this definition, "control" means (i) the
power, direct or indirect, to cause the direction or management of such
entity, whether by contract or otherwise, or (ii) ownership of fifty percent
(50%) or more of the outstanding shares, or (iii) beneficial ownership of
such entity.

"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.

"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation source, and
configuration files.

"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object
code, generated documentation, and conversions to other media types.

"Work" shall mean the work of authorship, whether in Source or Object form,
made available under the License, as indicated by a copyright notice that is
included in or attached to the work (an example is provided in the Appendix
below).

"Derivative Works" shall mean any work, whether in Source or Object form,
that is based on (or derived from) the Work and for which the editorial
revisions, annotations, elaborations, or other modifications represent, as a
whole, an original work of authorship. For the purposes of this License,
Derivative Works shall not include works that remain separable from, or
merely link (or bind by name) to the interfaces of, the Work and Derivative
Works thereof.

"Contribution" shall mean any work of authorship, including the original
version of the Work and any modifications or additions to that Work or
Derivative Works thereof, that is intentionally submitted to Licensor for
inclusion in the Work by the copyright owner or by an individual or Legal
Entity authorized to submit on behalf of the copyright owner. For the
purposes of this definition, "submitted" means any form of electronic, verbal,
or written communication sent to the Licensor or its representatives,
including but not limited to communication on electronic mailing lists, source
code control systems, and issue tracking systems that are managed by, or on
behalf of, the Licensor for the purpose of discussing and improving the Work,
but excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."

"Contributor" shall mean Licensor and any individual or Legal Entity on
behalf of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable copyright license to
reproduce, prepare Derivative Works of, publicly display, publicly perform,
sublicense, and distribute the Work and such Derivative Works in Source or
Object form.

3. Grant of Patent License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this
section) patent license to make, have made, use, offer to sell, sell, import,
and otherwise transfer the Work, where such license applies only to those
patent claims licensable by such Contributor that are necessarily infringed by
their Contribution(s) alone or by combination of their Contribution(s) with
the Work to which such Contribution(s) was submitted. If You institute patent
litigation against any entity (including a cross-claim or counterclaim in a
lawsuit) alleging that the Work or a Contribution incorporated within the Work
constitutes direct or contributory patent infringement, then any patent
licenses granted to You under this License for that Work shall terminate as of
the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the Work or
Derivative Works thereof in any medium, with or without modifications, and in
Source or Object form, provided that You meet the following conditions:

(a) You must give any other recipients of the Work or Derivative Works a copy
of this License; and

(b) You must cause any modified files to carry prominent notices stating that
You changed the files; and

(c) You must retain, in the Source form of any Derivative Works that You
distribute, all copyright, patent, trademark, and attribution notices from
the Source form of the Work, excluding those notices that do not pertain to
any part of the Derivative Works; and

(d) If the Work includes a "NOTICE" text file as part of its distribution,
then any Derivative Works that You distribute must include a readable copy of
the attribution notices contained within such NOTICE file, excluding those
notices that do not pertain to any part of the Derivative Works, in at least
one of the following places: within a NOTICE text file distributed as part of
the Derivative Works; within the Source form or documentation, if provided
along with the Derivative Works; or, within a display generated by the
Derivative Works, if and wherever such third-party notices normally appear.
The contents of the NOTICE file are for informational purposes only and do not
modify the License. You may add Your own attribution notices within Derivative
Works that You distribute, alongside or as an addendum to the NOTICE text from
the Work, provided that such additional attribution notices cannot be
construed as modifying the License.

You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a
whole, provided Your use, reproduction, and distribution of the Work otherwise
complies with the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise, any
Contribution intentionally submitted for inclusion in the Work by You to the
Licensor shall be under the terms and conditions of this License, without any
additional terms or conditions. Notwithstanding the above, nothing herein
shall supersede or modify the terms of any separate license agreement you may
have executed with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade names,
trademarks, service marks, or product names of the Licensor, except as
required for reasonable and customary use in describing the origin of the Work
and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
writing, Licensor provides the Work (and each Contributor provides its
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied, including, without limitation, any warranties
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any risks
associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory, whether in
tort (including negligence), contract, or otherwise, unless required by
applicable law (such as deliberate and grossly negligent acts) or agreed to in
writing, shall any Contributor be liable to You for damages, including any
direct, indirect, special, incidental, or consequential damages of any
character arising as a result of this License or out of the use or inability to
use the Work (including but not limited to damages for loss of goodwill, work
stoppage, computer failure or malfunction, or any and all other commercial
damages or losses), even if such Contributor has been advised of the
possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing the Work
or Derivative Works thereof, You may choose to offer, and charge a fee for,
acceptance of support, warranty, indemnity, or other liability obligations
and/or rights consistent with this License. However, in accepting such
obligations, You may act only on Your own behalf and on Your sole
responsibility, not on behalf of any other Contributor, and only if You agree
to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS


path: references/advanced-features.md
content: # Advanced Features

Advanced Workers capabilities for complex applications and enterprise use cases.

## Workers for Platforms

Deploy and manage customer-provided Workers on your infrastructure.

**Use cases:**
- SaaS platforms where customers write custom code
- Low-code/no-code platforms
- Plugin systems
- Custom business logic hosting

### Setup

**wrangler.toml:**

```toml
[[dispatch_namespaces]]
binding = "DISPATCHER"
namespace = "my-platform"

# Optional outbound Worker
outbound = { service = "my-outbound-worker" }
```

### Dynamic Dispatch

Route requests to customer Workers dynamically.

**Dispatch Worker:**

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const customerId = url.hostname.split(".")[0];

    // Get customer's Worker
    const userWorker = env.DISPATCHER.get(customerId);

    // Forward request to customer's Worker
    return userWorker.fetch(request);
  },
};
```

### Upload User Workers

**Via API:**

```typescript
async function uploadUserWorker(
  customerId: string,
  code: string,
  env: Env
): Promise<void> {
  const response = await fetch(
    `https://api.cloudflare.com/client/v4/accounts/${env.ACCOUNT_ID}/workers/dispatch/namespaces/${env.NAMESPACE}/scripts/${customerId}`,
    {
      method: "PUT",
      headers: {
        "Authorization": `Bearer ${env.API_TOKEN}`,
        "Content-Type": "application/javascript",
      },
      body: code,
    }
  );

  if (!response.ok) {
    throw new Error(`Failed to upload: ${await response.text()}`);
  }
}
```

**Via Wrangler:**

```bash
wrangler dispatch-namespace put my-platform customer-123 ./customer-worker.js
```

### Outbound Workers

Control what customer Workers can access.

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Validate request from customer Worker
    const url = new URL(request.url);

    // Block certain domains
    const blockedDomains = ["internal.example.com"];
    if (blockedDomains.some((d) => url.hostname.includes(d))) {
      return new Response("Forbidden", { status: 403 });
    }

    // Add authentication
    request.headers.set("X-Platform-Auth", env.PLATFORM_SECRET);

    // Forward to destination
    return fetch(request);
  },
};
```

### Limits and Quotas

Set limits for customer Workers.

```toml
[[dispatch_namespaces]]
binding = "DISPATCHER"
namespace = "my-platform"

[dispatch_namespaces.outbound]
service = "outbound-worker"
parameters = {
  cpu_ms = 50,
  requests = 1000
}
```

## Containers

Run Docker containers alongside Workers (public beta June 2025). Containers are built on Durable Objects, giving them global deployment, automatic scaling, and persistent state.

**Use cases:**
- Port existing Docker applications to Cloudflare
- Run any language or runtime (Python, Java, Go, etc.)
- Multi-GB memory workloads
- CLI tools and code sandboxes

### Container Class

```typescript
import { Container } from "cloudflare:workers";

export class MyContainer extends Container {
  // Port your container listens on
  defaultPort = 8080;

  // Auto-sleep after inactivity (saves costs)
  sleepAfter = "5 minutes";

  // Optional: override to run setup before container starts
  override async onStart(): Promise<void> {
    console.log("Container starting...");
  }
}
```

### Configuration

```toml
[[containers]]
class_name = "MyContainer"
image = "./Dockerfile"
max_instances = 5

[[durable_objects.bindings]]
class_name = "MyContainer"
name = "MY_CONTAINER"

[[migrations]]
tag = "v1"
new_classes = ["MyContainer"]
```

### Worker Usage

Workers act as orchestrator, service mesh, and API gateway to containers:

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Route to named container instance
    const id = env.MY_CONTAINER.idFromName("my-instance");
    const container = env.MY_CONTAINER.get(id);

    // Forward request - container receives it on defaultPort
    return container.fetch(request);
  },
};
```

## Workflows

Durable execution engine for multi-step, long-running tasks (GA April 2025). Steps automatically persist state and retry on failure.

**Use cases:**
- Order processing pipelines
- Human-in-the-loop approval flows
- Scheduled multi-step batch jobs
- Any process that needs to survive failures

### Workflow Class

```typescript
import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from "cloudflare:workers";

interface OrderParams {
  orderId: string;
  items: string[];
}

export class OrderWorkflow extends WorkflowEntrypoint<Env, OrderParams> {
  async run(event: WorkflowEvent<OrderParams>, step: WorkflowStep) {
    // Step 1: Validate (durable - retries on failure)
    const order = await step.do("validate", async () => {
      const result = await this.env.DB.prepare(
        "SELECT * FROM orders WHERE id = ?"
      ).bind(event.payload.orderId).first();
      if (!result) throw new Error("Order not found");
      return result;
    });

    // Step 2: Sleep without holding compute
    await step.sleep("processing-delay", "30 minutes");

    // Step 3: Wait for external event (webhook, human approval)
    const approval = await step.waitForEvent("await-approval", {
      timeout: "24 hours",
    });

    if (!approval.payload.approved) {
      await step.do("cancel", async () => {
        await this.env.DB.prepare(
          "UPDATE orders SET status = 'cancelled' WHERE id = ?"
        ).bind(event.payload.orderId).run();
      });
      return;
    }

    // Step 4: Fulfill
    await step.do("fulfill", async () => {
      await this.env.DB.prepare(
        "UPDATE orders SET status = 'fulfilled' WHERE id = ?"
      ).bind(event.payload.orderId).run();
    });
  }
}
```

### Configuration

```toml
[[workflows]]
name = "order-workflow"
binding = "ORDER_WORKFLOW"
class_name = "OrderWorkflow"
```

### Managing Workflow Instances

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Create instance
    const instance = await env.ORDER_WORKFLOW.create({
      params: { orderId: "123", items: ["item-1"] },
    });

    // Check status
    const status = await instance.status();

    // Send event to waiting step
    await instance.sendEvent({ approved: true });

    // List all instances
    const instances = await env.ORDER_WORKFLOW.list();

    return Response.json({ id: instance.id, status });
  },
};
```

## MCP Server Support

Host remote Model Context Protocol (MCP) servers on Workers using the Agents SDK.

```typescript
import { McpAgent } from "agents/mcp";

export class MyMcpServer extends McpAgent {
  server = new Server({ name: "my-mcp-server", version: "1.0.0" });

  async init() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [{
        name: "get_weather",
        description: "Get weather for a city",
        inputSchema: { type: "object", properties: { city: { type: "string" } } },
      }],
    }));

    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name === "get_weather") {
        return { content: [{ type: "text", text: "Sunny, 72F" }] };
      }
    });
  }
}
```

MCP servers on Workers support Streamable HTTP transport and can be secured with OAuth via the `workers-oauth-provider` package.

## Node.js Compatibility Expansion

Workers now supports a much broader set of Node.js APIs via the `nodejs_compat` flag:

| Module | Available Since | Notes |
|--------|----------------|-------|
| `node:net` | Jan 2025 | TCP `Socket` connections |
| `node:dns` | Jan 2025 | DNS resolution |
| `node:timers` | Jan 2025 | Node-compatible timers |
| `node:fs` | Sep 2025 | Virtual per-request file system (ephemeral) |
| `node:http` | Sep 2025 | Port Express-style apps with minimal changes |

**Compat flags:**
- `nodejs_compat` - enables all Node.js built-in modules
- `nodejs_compat_populate_process_env` - auto-populates `process.env` with text bindings
- `enable_nodejs_fs_module` - enables `node:fs` on compat dates before `2025-09-01`

With compat date `2025-09-01` or later, `node:fs` is enabled by default under `nodejs_compat`.

```typescript
// Example: Using node:fs (per-request virtual filesystem)
import { writeFileSync, readFileSync } from "node:fs";

export default {
  async fetch(request: Request): Promise<Response> {
    writeFileSync("/tmp/data.json", JSON.stringify({ key: "value" }));
    const data = readFileSync("/tmp/data.json", "utf-8");
    return new Response(data);
  },
};
```

## Cloudflare Realtime

Managed SFU (Selective Forwarding Unit) and TURN service for WebRTC audio/video (April 2025).

**Use cases:**
- Video/audio calls
- AI-powered voice agents
- Live streaming

Workers integrates with the RealtimeKit SDK for building real-time communication features. TURN service is global anycast and free when used with Realtime SFU.

- **Realtime Docs**: https://developers.cloudflare.com/realtime/

## Smart Placement

Automatically place Workers near data sources to reduce latency.

### Enable Smart Placement

**wrangler.toml:**

```toml
[placement]
mode = "smart"
```

**How it works:**
- Workers monitors where your Worker makes subrequests
- Automatically places future executions near those data sources
- Reduces round-trip time for database/API calls
- No code changes required

**Best for:**
- Database-heavy Workers
- Workers making many external API calls
- Geographically distributed data sources

**Limitations:**
- Not compatible with Durable Objects
- Requires Workers Standard plan

## WebSockets

Build real-time applications with WebSockets. Maximum message size is 32 MiB.

### Basic WebSocket Server

```typescript
export default {
  async fetch(request: Request): Promise<Response> {
    const upgradeHeader = request.headers.get("Upgrade");

    if (upgradeHeader !== "websocket") {
      return new Response("Expected WebSocket", { status: 426 });
    }

    const [client, server] = Object.values(new WebSocketPair());

    // Handle WebSocket messages
    server.accept();

    server.addEventListener("message", (event) => {
      console.log("Received:", event.data);

      // Echo message back
      server.send(`Echo: ${event.data}`);
    });

    server.addEventListener("close", (event) => {
      console.log("WebSocket closed:", event.code, event.reason);
    });

    server.addEventListener("error", (event) => {
      console.error("WebSocket error:", event);
    });

    return new Response(null, {
      status: 101,
      webSocket: client,
    });
  },
};
```

### WebSocket with Durable Objects

Use Durable Objects for coordinated WebSocket servers.

**Durable Object:**

```typescript
export class ChatRoom {
  state: DurableObjectState;
  sessions: Set<WebSocket>;

  constructor(state: DurableObjectState) {
    this.state = state;
    this.sessions = new Set();
  }

  async fetch(request: Request): Promise<Response> {
    const [client, server] = Object.values(new WebSocketPair());

    server.accept();
    this.sessions.add(server);

    server.addEventListener("message", (event) => {
      // Broadcast to all connected clients
      this.broadcast(event.data as string);
    });

    server.addEventListener("close", () => {
      this.sessions.delete(server);
    });

    return new Response(null, {
      status: 101,
      webSocket: client,
    });
  }

  broadcast(message: string) {
    for (const session of this.sessions) {
      try {
        session.send(message);
      } catch (error) {
        // Remove failed sessions
        this.sessions.delete(session);
      }
    }
  }
}
```

**Worker:**

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const roomId = url.pathname.substring(1) || "default";

    // Get Durable Object for this room
    const id = env.CHAT_ROOM.idFromName(roomId);
    const room = env.CHAT_ROOM.get(id);

    return room.fetch(request);
  },
};
```

### WebSocket Hibernation

Reduce costs by hibernating idle WebSocket connections.

```typescript
export class HibernatingChatRoom {
  state: DurableObjectState;

  constructor(state: DurableObjectState) {
    this.state = state;

    // Enable hibernation
    state.setWebSocketAutoResponse(
      new WebSocketRequestResponsePair("ping", "pong")
    );
  }

  async fetch(request: Request): Promise<Response> {
    const [client, server] = Object.values(new WebSocketPair());

    // Accept with hibernation
    this.state.acceptWebSocket(server);

    return new Response(null, {
      status: 101,
      webSocket: client,
    });
  }

  async webSocketMessage(ws: WebSocket, message: string) {
    // Called when message received (Worker woken up)
    const data = JSON.parse(message);

    // Broadcast to all
    this.state.getWebSockets().forEach((socket) => {
      socket.send(message);
    });
  }

  async webSocketClose(ws: WebSocket, code: number, reason: string) {
    // Cleanup on close
    ws.close(code, reason);
  }
}
```

## Streaming

Stream responses for large datasets.

### Streaming JSON

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const { readable, writable } = new TransformStream();
    const writer = writable.getWriter();
    const encoder = new TextEncoder();

    // Stream in background
    (async () => {
      try {
        await writer.write(encoder.encode("["));

        const results = await env.DB.prepare(
          "SELECT * FROM large_table"
        ).all();

        for (let i = 0; i < results.results.length; i++) {
          const item = results.results[i];
          await writer.write(encoder.encode(JSON.stringify(item)));

          if (i < results.results.length - 1) {
            await writer.write(encoder.encode(","));
          }
        }

        await writer.write(encoder.encode("]"));
        await writer.close();
      } catch (error) {
        await writer.abort(error);
      }
    })();

    return new Response(readable, {
      headers: { "Content-Type": "application/json" },
    });
  },
};
```

### Server-Sent Events (SSE)

```typescript
export default {
  async fetch(request: Request): Promise<Response> {
    const { readable, writable } = new TransformStream();
    const writer = writable.getWriter();
    const encoder = new TextEncoder();

    // Send events in background
    (async () => {
      try {
        for (let i = 0; i < 10; i++) {
          await writer.write(
            encoder.encode(`data: ${JSON.stringify({ count: i })}\n\n`)
          );
          await new Promise((resolve) => setTimeout(resolve, 1000));
        }

        await writer.close();
      } catch (error) {
        await writer.abort(error);
      }
    })();

    return new Response(readable, {
      headers: {
        "Content-Type": "text/event-stream",
        "Cache-Control": "no-cache",
        "Connection": "keep-alive",
      },
    });
  },
};
```

## Custom Domains

Configure custom domains for Workers.

### Via Wrangler

```toml
routes = [
  { pattern = "api.example.com/*", zone_name = "example.com" }
]
```

### Via Dashboard

1. Navigate to Workers & Pages
2. Select your Worker
3. Go to Settings > Triggers
4. Add Custom Domain

### Multiple Domains

```toml
routes = [
  { pattern = "api.example.com/*", zone_name = "example.com" },
  { pattern = "api.other-domain.com/*", zone_name = "other-domain.com" }
]
```

## Static Assets

Serve static files with Workers. Limits: 100,000 assets per Worker version on Paid plans (20,000 on Free), 25 MiB per file.

### Configuration

**wrangler.toml:**

```toml
[assets]
directory = "./public"
binding = "ASSETS"

# HTML handling
html_handling = "auto-trailing-slash"

# 404 handling
not_found_handling = "single-page-application"
```

### Custom Asset Handling

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // API routes
    if (url.pathname.startsWith("/api/")) {
      return handleAPI(request, env);
    }

    // Static assets
    try {
      const asset = await env.ASSETS.fetch(request);

      // Add custom headers
      const response = new Response(asset.body, asset);
      response.headers.set("X-Custom-Header", "value");

      return response;
    } catch {
      return new Response("Not found", { status: 404 });
    }
  },
};
```

### Framework Integration

**Next.js:**

```bash
npm create cloudflare@latest my-next-app -- --framework=next
```

**Remix:**

```bash
npm create cloudflare@latest my-remix-app -- --framework=remix
```

**Astro:**

```bash
npm create cloudflare@latest my-astro-app -- --framework=astro
```

## TCP Sockets

Connect to external services via TCP.

```typescript
export default {
  async fetch(request: Request): Promise<Response> {
    const socket = connect({
      hostname: "example.com",
      port: 6379, // Redis
    });

    const writer = socket.writable.getWriter();
    const encoder = new TextEncoder();

    // Send Redis command
    await writer.write(encoder.encode("PING\r\n"));

    // Read response
    const reader = socket.readable.getReader();
    const { value } = await reader.read();
    const response = new TextDecoder().decode(value);

    return Response.json({ response });
  },
};
```

## HTML Rewriter

Transform HTML on the fly.

```typescript
class LinkRewriter {
  element(element: Element) {
    const href = element.getAttribute("href");

    if (href && href.startsWith("/")) {
      // Make absolute
      element.setAttribute("href", `https://example.com${href}`);
    }

    // Add tracking
    element.setAttribute("data-tracked", "true");
  }
}

export default {
  async fetch(request: Request): Promise<Response> {
    const response = await fetch(request);

    return new HTMLRewriter()
      .on("a", new LinkRewriter())
      .on("img", {
        element(element) {
          // Lazy load images
          element.setAttribute("loading", "lazy");
        },
      })
      .transform(response);
  },
};
```

## Scheduled Events (Cron)

Run Workers on a schedule.

**wrangler.toml:**

```toml
[triggers]
crons = [
  "0 0 * * *",     # Daily at midnight
  "*/15 * * * *",  # Every 15 minutes
  "0 9 * * 1-5"    # Weekdays at 9 AM
]
```

**Handler:**

```typescript
export default {
  async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext) {
    console.log("Cron trigger:", event.cron);

    // Cleanup old data
    await env.DB.prepare("DELETE FROM logs WHERE created_at < ?")
      .bind(Date.now() - 30 * 24 * 60 * 60 * 1000)
      .run();

    // Send daily report
    ctx.waitUntil(sendDailyReport(env));
  },
};
```

## Email Handler

Process incoming emails.

**wrangler.toml:**

```toml
[email]
name = "[REDACTED_SECRET]"
```

**Handler:**

```typescript
export default {
  async email(message: ForwardableEmailMessage, env: Env) {
    console.log("From:", message.from);
    console.log("Subject:", message.headers.get("subject"));

    // Forward to support team
    await message.forward("[REDACTED_SECRET]");

    // Or process email
    const rawEmail = await new Response(message.raw).text();
    await env.EMAILS.put(message.headers.get("message-id"), rawEmail);
  },
};
```

## Tail Workers

Monitor and log other Workers.

**wrangler.toml:**

```toml
[tail_consumers]
service = "my-logging-worker"
```

**Tail Worker:**

```typescript
export default {
  async tail(events: TraceItem[], env: Env) {
    for (const event of events) {
      if (event.outcome === "exception") {
        // Log errors to external service
        await fetch("https://logs.example.com", {
          method: "POST",
          body: JSON.stringify({
            scriptName: event.scriptName,
            error: event.exceptions,
            timestamp: event.event.request.timestamp,
          }),
        });
      }
    }
  },
};
```

## Multi-Region Deployments

Deploy Workers to specific regions (Enterprise only).

```toml
[placement]
mode = "regional"
regions = ["us-east", "eu-west"]
```

## Workers Analytics Engine

Write custom metrics.

```toml
[[analytics_engine_datasets]]
binding = "ANALYTICS"
```

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const start = Date.now();
    const response = await handleRequest(request, env);
    const duration = Date.now() - start;

    // Write custom metrics
    env.ANALYTICS.writeDataPoint({
      blobs: [request.url, request.method, String(response.status)],
      doubles: [duration],
      indexes: [request.headers.get("user-agent") || "unknown"],
    });

    return response;
  },
};
```

## Additional Resources

- **Containers**: https://developers.cloudflare.com/containers/
- **Workflows**: https://developers.cloudflare.com/workflows/
- **MCP Servers**: https://developers.cloudflare.com/agents/model-context-protocol/
- **Workers for Platforms**: https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/
- **Smart Placement**: https://developers.cloudflare.com/workers/configuration/smart-placement/
- **WebSockets**: https://developers.cloudflare.com/workers/runtime-apis/websockets/
- **Static Assets**: https://developers.cloudflare.com/workers/static-assets/
- **Scheduled Events**: https://developers.cloudflare.com/workers/configuration/cron-triggers/
- **Node.js Compat**: https://developers.cloudflare.com/workers/runtime-apis/nodejs/


path: references/bindings-complete-guide.md
content: # Complete Bindings Guide

Bindings are how Workers connect to Cloudflare resources and external services. They provide zero-latency access to storage, databases, queues, and other services.

## Storage Bindings

### KV (Key-Value Storage)

Global, low-latency, eventually consistent key-value storage.

**Best for:**
- Configuration data
- User sessions
- Cache
- Small objects (<25 MB)

**Configuration:**

```toml
[[kv_namespaces]]
binding = "MY_KV"
id = "your-namespace-id"
preview_id = "preview-namespace-id"  # For local dev
```

**API:**

```typescript
// Write
await env.MY_KV.put("key", "value");
await env.MY_KV.put("key", "value", {
  expirationTtl: 3600,  // Expire in 1 hour
  metadata: { user: "123" }
});

// Read
const value = await env.MY_KV.get("key");
const json = await env.MY_KV.get("key", "json");
const buffer = await env.MY_KV.get("key", "arrayBuffer");

// With metadata
const { value, metadata } = await env.MY_KV.getWithMetadata("key");

// Delete
await env.MY_KV.delete("key");

// List keys
const keys = await env.MY_KV.list({ prefix: "user:" });
```

**Limits:**
- Key size: 512 bytes
- Value size: 25 MB
- Write rate: 1 write/second per key (eventually consistent)
- Read rate: unlimited

### D1 (SQL Database)

Serverless SQLite database built on SQLite.

**Best for:**
- Structured data
- Relational data
- Complex queries
- ACID transactions

**Configuration:**

```toml
[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "your-database-id"
```

**API:**

```typescript
// Query with bind parameters
const result = await env.DB.prepare(
  "SELECT * FROM users WHERE id = ?"
).bind(userId).all();

// Insert
await env.DB.prepare(
  "INSERT INTO users (name, email) VALUES (?, ?)"
).bind(name, email).run();

// Update
await env.DB.prepare(
  "UPDATE users SET last_login = ? WHERE id = ?"
).bind(new Date().toISOString(), userId).run();

// Transaction
const results = await env.DB.batch([
  env.DB.prepare("INSERT INTO users (name) VALUES (?)").bind("Alice"),
  env.DB.prepare("INSERT INTO users (name) VALUES (?)").bind("Bob"),
]);

// First row only
const user = await env.DB.prepare(
  "SELECT * FROM users WHERE id = ?"
).bind(userId).first();
```

**Read Replication (GA April 2025):**

D1 supports global read replicas for low-latency reads. Use the Sessions API for sequential consistency:

```typescript
// Consistent reads after writes
const bookmark = request.headers.get("x-d1-bookmark") ?? "first-unconstrained";
const session = env.DB.withSession(bookmark);
const result = await session.prepare("SELECT * FROM users").all();

// Return bookmark for subsequent requests
const responseBookmark = result.meta?.bookmark;
```

Session modes: `"first-primary"` (always read primary), `"first-unconstrained"` (read any replica).

**Features:**
- Read replication (global read replicas, no extra cost)
- Time Travel (restore to any point in last 30 days)
- Backups
- Migrations via Wrangler

**Limits:**
- Database size: 10 GB (Paid), 500 MB (Free)
- Rows read: 25M/day (Paid), 5M/day (Free)
- Rows written: 50M/day (Paid), 100K/day (Free)

### R2 (Object Storage)

S3-compatible object storage with zero egress fees.

**Best for:**
- Large files
- Media storage
- Static assets
- Backups

**Configuration:**

```toml
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"
jurisdiction = "eu"  # Optional: eu or fedramp
```

**API:**

```typescript
// Put object
await env.MY_BUCKET.put("file.txt", "contents", {
  httpMetadata: {
    contentType: "text/plain",
    cacheControl: "max-age=3600",
  },
  customMetadata: {
    user: "123",
  },
});

// Put from stream
await env.MY_BUCKET.put("large-file.bin", request.body);

// Get object
const object = await env.MY_BUCKET.get("file.txt");
if (object) {
  const text = await object.text();
  const buffer = await object.arrayBuffer();
  const stream = object.body; // ReadableStream

  // Metadata
  console.log(object.httpMetadata);
  console.log(object.customMetadata);
}

// Get with range
const object = await env.MY_BUCKET.get("file.txt", {
  range: { offset: 0, length: 1024 }
});

// Head (metadata only)
const object = await env.MY_BUCKET.head("file.txt");

// Delete
await env.MY_BUCKET.delete("file.txt");

// List objects
const objects = await env.MY_BUCKET.list({
  prefix: "images/",
  limit: 1000,
});

// Multipart upload (for large files)
const upload = await env.MY_BUCKET.createMultipartUpload("large.bin");
const part = await upload.uploadPart(1, data);
await upload.complete([part]);
```

**Features:**
- Automatic multipart uploads
- Object versioning
- Event notifications
- Public buckets
- Custom domains

**Limits:**
- Max object size: 5 TB
- Storage: unlimited
- Operations: unlimited

## Compute Bindings

### Durable Objects

Strongly consistent, coordinated stateful objects with SQLite storage. Available on Free and Paid plans (free tier added April 2025).

**Best for:**
- Real-time collaboration
- WebSocket servers
- Coordination
- Strong consistency
- Per-user/per-room state

**Configuration:**

```toml
[[durable_objects.bindings]]
name = "COUNTER"
class_name = "Counter"
script_name = "my-worker"  # Optional: if in different Worker

[[migrations]]
tag = "v1"
new_classes = ["Counter"]
```

**Durable Object Class:**

```typescript
export class Counter {
  state: DurableObjectState;

  constructor(state: DurableObjectState, env: Env) {
    this.state = state;
  }

  async fetch(request: Request): Promise<Response> {
    // Get from storage
    let count = (await this.state.storage.get("count")) || 0;

    // Increment
    count++;

    // Put to storage
    await this.state.storage.put("count", count);

    return Response.json({ count });
  }

  // Alarms (scheduled actions)
  async alarm() {
    await this.state.storage.delete("count");
  }
}
```

**Worker Usage:**

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Get Durable Object ID
    const id = env.COUNTER.idFromName("global-counter");

    // Get stub (reference)
    const stub = env.COUNTER.get(id);

    // Call via fetch
    return stub.fetch(request);
  },
};
```

**Storage API:**

```typescript
// Single operations
await this.state.storage.put("key", "value");
const value = await this.state.storage.get("key");
await this.state.storage.delete("key");

// Batch operations
await this.state.storage.put({
  key1: "value1",
  key2: "value2",
});

const values = await this.state.storage.get(["key1", "key2"]);

// List
const entries = await this.state.storage.list();

// SQL (new)
const result = await this.state.storage.sql.exec(
  "SELECT * FROM users WHERE id = ?", userId
);
```

**Features:**
- SQLite-backed storage (GA)
- Automatic persistence
- Alarms (scheduled actions)
- WebSocket Hibernation
- Point-in-time recovery
- Available on Free plan (April 2025)

### Queues

Message queuing for async processing with guaranteed delivery.

**Best for:**
- Background jobs
- Async processing
- Decoupling services
- Retry logic

**Configuration:**

```toml
[[queues.producers]]
binding = "MY_QUEUE"
queue = "my-queue"

[[queues.consumers]]
queue = "my-queue"
max_batch_size = 100
max_batch_timeout = 30
```

**Producer (send messages):**

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Send single message
    await env.MY_QUEUE.send({ userId: 123, action: "process" });

    // Send batch
    await env.MY_QUEUE.sendBatch([
      { body: { userId: 123 } },
      { body: { userId: 456 } },
    ]);

    return Response.json({ queued: true });
  },
};
```

**Consumer (receive messages):**

```typescript
export default {
  async queue(batch: MessageBatch<any>, env: Env): Promise<void> {
    for (const message of batch.messages) {
      try {
        await processMessage(message.body);
        message.ack();  // Mark as processed
      } catch (error) {
        message.retry();  // Retry later
      }
    }
  },
};
```

**Features:**
- Guaranteed delivery
- Automatic retries
- Dead letter queues
- Batch processing
- Pull consumers (API-based)

### Workflows

Durable execution engine for multi-step, long-running tasks (GA April 2025).

**Best for:**
- Multi-step processes with automatic retries
- Human-in-the-loop approvals
- Long-running background jobs
- Tasks that need state persistence across failures

**Configuration:**

```toml
[[workflows]]
name = "order-workflow"
binding = "ORDER_WORKFLOW"
class_name = "OrderWorkflow"
```

**Workflow Class:**

```typescript
import { WorkflowEntrypoint, WorkflowStep, WorkflowEvent } from "cloudflare:workers";

export class OrderWorkflow extends WorkflowEntrypoint<Env, Params> {
  async run(event: WorkflowEvent<Params>, step: WorkflowStep) {
    // Each step is durable - survives restarts
    const order = await step.do("validate-order", async () => {
      return validateOrder(event.payload);
    });

    // Sleep without holding compute
    await step.sleep("wait-for-processing", "1 hour");

    // Wait for external event (human approval, webhook, etc.)
    const approval = await step.waitForEvent("await-approval", {
      timeout: "24 hours",
    });

    await step.do("fulfill-order", async () => {
      return fulfillOrder(order, approval);
    });
  }
}
```

**Worker Usage:**

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Create workflow instance
    const instance = await env.ORDER_WORKFLOW.create({
      params: { orderId: "123", items: ["item-1"] },
    });

    // Get status
    const status = await instance.status();

    // Send event to waiting workflow
    await instance.sendEvent({ approved: true });

    return Response.json({ id: instance.id, status });
  },
};
```

### Containers

Run Docker containers alongside Workers (public beta June 2025). Built on Durable Objects.

**Best for:**
- Porting existing Docker applications
- Running any language/runtime
- Multi-GB memory workloads
- CLI tools and code sandboxes

**Configuration:**

```toml
[[containers]]
class_name = "MyContainer"
image = "./Dockerfile"
max_instances = 5

[[durable_objects.bindings]]
class_name = "MyContainer"
name = "MY_CONTAINER"
```

**Container Class:**

```typescript
import { Container } from "cloudflare:workers";

export class MyContainer extends Container {
  defaultPort = 8080;

  // Optional: auto-sleep after inactivity
  sleepAfter = "5 minutes";
}
```

**Worker Usage:**

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const id = env.MY_CONTAINER.idFromName("my-instance");
    const container = env.MY_CONTAINER.get(id);
    return container.fetch(request);
  },
};
```

## AI & ML Bindings

### Workers AI

Run AI models directly from Workers.

**Best for:**
- Text generation (LLMs)
- Embeddings
- Image generation
- Speech recognition
- Translation

**Configuration:**

```toml
[ai]
binding = "AI"
```

**API:**

```typescript
// Text generation (with speculative decoding for 2-4x speed)
const response = await env.AI.run("@cf/meta/llama-3.3-70b-instruct-fp8-fast", {
  messages: [
    { role: "system", content: "You are a helpful assistant" },
    { role: "user", content: "What is Cloudflare?" }
  ],
});

// Embeddings (multilingual with bge-m3, or English with bge-base-en-v1.5)
const embeddings = await env.AI.run("@cf/baai/bge-m3", {
  text: "The quick brown fox jumps over the lazy dog",
});

// Reranking (for RAG post-processing)
const reranked = await env.AI.run("@cf/baai/bge-reranker-base", {
  query: "What is Workers?",
  documents: ["Workers runs on V8", "R2 is object storage"],
});

// Image generation
const image = await env.AI.run("@cf/stabilityai/stable-diffusion-xl-base-1.0", {
  prompt: "A sunset over the ocean",
});

// Speech to text
const result = await env.AI.run("@cf/openai/whisper-large-v3-turbo", {
  audio: audioData,
});

// Text to speech
const audio = await env.AI.run("@cf/myshell-ai/melotts", {
  text: "Hello from Cloudflare Workers",
});

// Streaming
const stream = await env.AI.run("@cf/meta/llama-3.3-70b-instruct-fp8-fast", {
  messages: [{ role: "user", content: "Tell me a story" }],
  stream: true,
});
```

**Async Batch API:** Submit large workloads asynchronously instead of waiting for each inference call synchronously. Useful for batch processing embeddings or large document sets.

### Vectorize

Vector database for similarity search.

**Best for:**
- Semantic search
- RAG (Retrieval Augmented Generation)
- Recommendations
- Embeddings storage

**Configuration:**

```toml
[[vectorize]]
binding = "VECTORIZE"
index_name = "my-index"
```

**API:**

```typescript
// Insert vectors
await env.VECTORIZE.insert([
  { id: "1", values: [0.1, 0.2, ...], metadata: { text: "..." } },
  { id: "2", values: [0.3, 0.4, ...], metadata: { text: "..." } },
]);

// Query (similarity search)
const results = await env.VECTORIZE.query(
  [0.15, 0.25, ...],  // Query vector
  {
    topK: 5,
    returnMetadata: true,
  }
);

// With metadata filtering
const results = await env.VECTORIZE.query(vector, {
  topK: 5,
  filter: { category: "technology" },
});
```

## Database Bindings

### Hyperdrive

Accelerate access to existing databases via connection pooling and caching.

**Best for:**
- Connecting to existing Postgres/MySQL
- Reducing latency to traditional databases
- Connection pooling

**Configuration:**

```toml
[[hyperdrive]]
binding = "HYPERDRIVE"
id = "your-hyperdrive-id"
```

**Usage with postgres:**

```typescript
import { Client } from "pg";

const client = new Client({
  connectionString: env.HYPERDRIVE.connectionString,
});

await client.connect();
const result = await client.query("SELECT * FROM users");
await client.end();
```

**Features:**
- Connection pooling
- Query caching
- Read replicas support

## Service Bindings

Call other Workers via RPC or HTTP. For private network access, see Workers VPC Service Bindings below.

**Configuration:**

```toml
[[services]]
binding = "AUTH_SERVICE"
service = "auth-worker"
environment = "production"
```

**HTTP-based:**

```typescript
const response = await env.AUTH_SERVICE.fetch(new Request("http://auth/verify"));
```

**RPC-based (recommended):**

```typescript
// In auth-worker
export class AuthService extends WorkerEntrypoint {
  async verifyToken([REDACTED_SECRET]): Promise<boolean> {
    // Verify logic
    return true;
  }
}

// In calling worker
const isValid = await env.AUTH_SERVICE.verifyToken(token);
```

## Additional Bindings

### Analytics Engine

Write custom analytics and metrics.

```toml
[[analytics_engine_datasets]]
binding = "ANALYTICS"
```

```typescript
env.ANALYTICS.writeDataPoint({
  blobs: ["user-123", "click"],
  doubles: [1.5],
  indexes: ["button-1"],
});
```

### Browser Rendering

Control headless browsers.

```toml
browser = { binding = "BROWSER" }
```

```typescript
const browser = await puppeteer.launch(env.BROWSER);
const page = await browser.newPage();
await page.goto("https://example.com");
const screenshot = await page.screenshot();
```

### Rate Limiting

Built-in rate limiting.

```toml
[[unsafe.bindings]]
name = "RATE_LIMITER"
type = "ratelimit"
namespace_id = "your-namespace-id"
simple = { limit = 100, period = 60 }
```

```typescript
const { success } = await env.RATE_LIMITER.limit({ key: userId });
if (!success) {
  return new Response("Rate limited", { status: 429 });
}
```

### mTLS

Present client certificates.

```toml
[[mtls_certificates]]
binding = "CERT"
certificate_id = "your-cert-id"
```

```typescript
const response = await fetch("https://api.example.com", {
  certificate: env.CERT,
});
```

### AI Gateway

Proxy and manage AI API calls with logging, caching, and rate limiting.

```toml
[ai.gateway]
binding = "AI_GATEWAY"
gateway_id = "my-gateway"
```

```typescript
// Universal request to any AI provider via gateway
const response = await env.AI_GATEWAY.run("openai/chat/completions", {
  model: "gpt-4",
  messages: [{ role: "user", content: "Hello" }],
});

// Retrieve log information
const log = await env.AI_GATEWAY.getLog(logId);

// Send feedback and update metadata
await env.AI_GATEWAY.patchLog(logId, { feedback: "good", metadata: { user: "123" } });
```

### Secrets Store

Account-level centralized secrets shared across Workers (April 2025).

```toml
[[secrets_store_secrets]]
binding = "API_KEY"
store_id = "abc123"
secret_name = "open_ai_key"  # pragma: allowlist secret
```

```typescript
const key = await env.API_KEY.get();
```

Unlike per-Worker secrets, Secrets Store entries are managed centrally and reusable across all Workers in your account.

### AutoRAG / AI Search

Managed RAG pipeline - upload docs to R2, AutoRAG handles embeddings, indexing, retrieval, and generation (open beta April 2025).

```toml
[[autorag]]
binding = "AI_SEARCH"
name = "my-autorag-instance"
```

```typescript
const answer = await env.AI_SEARCH.aiSearch("What is the refund policy?");
// Returns generated answer with source citations
```

AutoRAG continuously syncs with your R2 data source, so updates are automatic.

### Pipelines

Streaming data ingestion to R2 as Apache Iceberg or Parquet/JSON files (beta).

```toml
[[pipelines]]
binding = "MY_PIPELINE"
pipeline = "my-clickstream-pipeline"
```

```typescript
await env.MY_PIPELINE.send({
  event: "page_view",
  path: "/pricing",
  timestamp: Date.now(),
});
```

Supports up to 100 MB/second per pipeline. Data is auto-batched and delivered to R2.

### Workers VPC Service Bindings

Connect Workers to private APIs inside AWS, GCP, Azure, or on-premise via Cloudflare Tunnel (beta November 2025).

```toml
[[vpc_services]]
binding = "PRIVATE_API"
service_id = "<your-vpc-service-id>"
```

```typescript
const response = await env.PRIVATE_API.fetch("https://internal-api/data");
```

## Best Practices

### Binding Selection

- **KV**: Configuration, sessions, cache
- **D1**: Structured data, complex queries
- **R2**: Large files, media, backups
- **Durable Objects**: Real-time, strong consistency, coordination
- **Queues**: Background jobs, async processing
- **Workflows**: Multi-step durable processes, long-running tasks
- **Containers**: Docker workloads, any language/runtime
- **Workers AI**: AI/ML inference
- **Vectorize**: Similarity search, RAG
- **AutoRAG**: Managed RAG pipeline over R2 documents

### Performance

- Use `ctx.waitUntil()` for non-critical writes
- Batch operations when possible
- Use appropriate consistency models
- Cache frequently accessed data

### Error Handling

Always handle errors from bindings:

```typescript
try {
  const value = await env.MY_KV.get("key");
} catch (error) {
  // Handle error
  console.error("KV error:", error);
  return new Response("Service unavailable", { status: 503 });
}
```

### Local Development

Use Wrangler for local testing with bindings:

```bash
# Create resources
wrangler d1 create my-database
wrangler kv:namespace create MY_KV

# Local dev (uses local storage by default in Wrangler v4)
wrangler dev

# Local dev with real remote bindings (GA September 2025)
wrangler dev --remote
```

## Additional Resources

- **Bindings Reference**: https://developers.cloudflare.com/workers/runtime-apis/bindings/
- **KV**: https://developers.cloudflare.com/kv/
- **D1**: https://developers.cloudflare.com/d1/
- **R2**: https://developers.cloudflare.com/r2/
- **Durable Objects**: https://developers.cloudflare.com/durable-objects/
- **Queues**: https://developers.cloudflare.com/queues/
- **Workers AI**: https://developers.cloudflare.com/workers-ai/
- **Vectorize**: https://developers.cloudflare.com/vectorize/
- **Workflows**: https://developers.cloudflare.com/workflows/
- **Containers**: https://developers.cloudflare.com/containers/


path: references/development-patterns.md
content: # Development Best Practices

Patterns and best practices for building robust, maintainable Workers applications.

## Testing

### Vitest Integration

Workers has first-class Vitest integration for unit and integration testing.

**Setup:**

```bash
npm install -D vitest @cloudflare/vitest-pool-workers
```

**vitest.config.ts:**

```typescript
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";

export default defineWorkersConfig({
  test: {
    poolOptions: {
      workers: {
        wrangler: { configPath: "./wrangler.toml" },
      },
    },
  },
});
```

**Basic Test:**

```typescript
import { env, createExecutionContext, waitOnExecutionContext } from "cloudflare:test";
import { describe, it, expect } from "vitest";
import worker from "./index";

describe("Worker", () => {
  it("responds with JSON", async () => {
    const request = new Request("http://example.com/api/users");
    const ctx = createExecutionContext();
    const response = await worker.fetch(request, env, ctx);
    await waitOnExecutionContext(ctx);

    expect(response.status).toBe(200);
    const data = await response.json();
    expect(data).toHaveProperty("users");
  });

  it("handles errors gracefully", async () => {
    const request = new Request("http://example.com/api/error");
    const ctx = createExecutionContext();
    const response = await worker.fetch(request, env, ctx);

    expect(response.status).toBe(500);
  });
});
```

**Testing with Bindings:**

```typescript
import { env } from "cloudflare:test";

describe("KV operations", () => {
  it("reads and writes to KV", async () => {
    // env provides access to bindings configured in wrangler.toml
    await env.MY_KV.put("test-key", "test-value");
    const value = await env.MY_KV.get("test-key");
    expect(value).toBe("test-value");
  });
});
```

**Testing Durable Objects:**

```typescript
import { env, runInDurableObject } from "cloudflare:test";

describe("Counter Durable Object", () => {
  it("increments counter", async () => {
    await runInDurableObject(env.COUNTER, async (instance, state) => {
      const request = new Request("http://do/increment");
      const response = await instance.fetch(request);
      const data = await response.json();
      expect(data.count).toBe(1);
    });
  });
});
```

**Run Tests:**

```bash
npm test
# or
npx vitest
```

### Integration Testing

Test full request/response cycles with external services.

```typescript
describe("External API integration", () => {
  it("fetches data from external API", async () => {
    const request = new Request("http://example.com/api/external");
    const ctx = createExecutionContext();
    const response = await worker.fetch(request, env, ctx);

    expect(response.status).toBe(200);

    // Verify external API was called correctly
    const data = await response.json();
    expect(data).toHaveProperty("externalData");
  });
});
```

### Mocking

Mock external dependencies for isolated tests.

```typescript
import { vi } from "vitest";

describe("Mocked fetch", () => {
  it("handles fetch errors", async () => {
    // Mock global fetch
    global.fetch = vi.fn().mockRejectedValue(new Error("Network error"));

    const request = new Request("http://example.com/api/data");
    const ctx = createExecutionContext();
    const response = await worker.fetch(request, env, ctx);

    expect(response.status).toBe(503);
  });
});
```

## Error Handling

### Global Error Handling

Catch all errors and return appropriate responses.

```typescript
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    try {
      return await handleRequest(request, env, ctx);
    } catch (error) {
      console.error("Uncaught error:", error);

      return Response.json(
        {
          error: "Internal server error",
          message: error instanceof Error ? error.message : "Unknown error",
        },
        { status: 500 }
      );
    }
  },
};

async function handleRequest(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
  // Your handler logic
}
```

### Custom Error Classes

Define custom error types for better error handling.

```typescript
class NotFoundError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "NotFoundError";
  }
}

class UnauthorizedError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "UnauthorizedError";
  }
}

class ValidationError extends Error {
  constructor(public fields: Record<string, string>) {
    super("Validation failed");
    this.name = "ValidationError";
  }
}

async function handleRequest(request: Request, env: Env): Promise<Response> {
  try {
    // Your logic
    const user = await getUser(userId);
    if (!user) {
      throw new NotFoundError("User not found");
    }

    return Response.json(user);
  } catch (error) {
    if (error instanceof NotFoundError) {
      return Response.json({ error: error.message }, { status: 404 });
    }

    if (error instanceof UnauthorizedError) {
      return Response.json({ error: error.message }, { status: 401 });
    }

    if (error instanceof ValidationError) {
      return Response.json(
        { error: "Validation failed", fields: error.fields },
        { status: 400 }
      );
    }

    // Unknown error
    console.error("Unexpected error:", error);
    return Response.json({ error: "Internal server error" }, { status: 500 });
  }
}
```

### Retry Logic

Implement retry logic for transient failures.

```typescript
async function fetchWithRetry(
  url: string,
  options: RequestInit = {},
  maxRetries = 3
): Promise<Response> {
  let lastError: Error;

  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url, options);

      if (response.ok) {
        return response;
      }

      // Don't retry on client errors (4xx)
      if (response.status >= 400 && response.status < 500) {
        return response;
      }

      lastError = new Error(`HTTP ${response.status}`);
    } catch (error) {
      lastError = error as Error;
    }

    // Exponential backoff
    if (i < maxRetries - 1) {
      await new Promise((resolve) => setTimeout(resolve, Math.pow(2, i) * 1000));
    }
  }

  throw lastError!;
}
```

## Performance Optimization

### Caching Strategies

Use the Cache API effectively.

```typescript
async function handleCachedRequest(request: Request): Promise<Response> {
  const cache = caches.default;
  const cacheKey = new Request(request.url, request);

  // Try to get from cache
  let response = await cache.match(cacheKey);

  if (!response) {
    // Cache miss - fetch from origin
    response = await fetchFromOrigin(request);

    // Cache successful responses
    if (response.ok) {
      response = new Response(response.body, {
        ...response,
        headers: {
          ...Object.fromEntries(response.headers),
          "Cache-Control": "public, max-age=3600",
        },
      });

      // Don't await - cache in background
      ctx.waitUntil(cache.put(cacheKey, response.clone()));
    }
  }

  return response;
}
```

**Cache with custom keys:**

```typescript
function getCacheKey(request: Request, userId?: string): Request {
  const url = new URL(request.url);

  // Include user ID in cache key for personalized content
  if (userId) {
    url.searchParams.set("userId", userId);
  }

  return new Request(url.toString(), request);
}
```

### Response Streaming

Stream responses for large data.

```typescript
export default {
  async fetch(request: Request): Promise<Response> {
    const { readable, writable } = new TransformStream();
    const writer = writable.getWriter();

    // Stream data in background
    (async () => {
      try {
        const data = await fetchLargeDataset();

        for (const item of data) {
          await writer.write(new TextEncoder().encode(JSON.stringify(item) + "\n"));
        }

        await writer.close();
      } catch (error) {
        await writer.abort(error);
      }
    })();

    return new Response(readable, {
      headers: {
        "Content-Type": "application/x-ndjson",
        "Transfer-Encoding": "chunked",
      },
    });
  },
};
```

### Batching Operations

Batch multiple operations for better performance.

```typescript
// Bad: Sequential operations
for (const userId of userIds) {
  await env.KV.get(`user:${userId}`);
}

// Good: Batch operations
const users = await Promise.all(
  userIds.map((id) => env.KV.get(`user:${id}`))
);

// Even better: Use batch APIs when available
const results = await env.DB.batch([
  env.DB.prepare("SELECT * FROM users WHERE id = ?").bind(1),
  env.DB.prepare("SELECT * FROM users WHERE id = ?").bind(2),
  env.DB.prepare("SELECT * FROM users WHERE id = ?").bind(3),
]);
```

### Background Tasks

Use `ctx.waitUntil()` for non-critical work.

```typescript
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    // Process request
    const response = await handleRequest(request, env);

    // Log analytics in background (don't block response)
    ctx.waitUntil(
      env.ANALYTICS.writeDataPoint({
        blobs: [request.url, request.method],
        doubles: [performance.now()],
      })
    );

    // Update cache in background
    ctx.waitUntil(updateCache(request, response, env));

    return response;
  },
};
```

## Debugging

### Console Logging

Use console methods for debugging.

```typescript
console.log("Info:", { userId, action });
console.error("Error occurred:", error);
console.warn("Deprecated API used");
console.debug("Debug info:", data);

// Structured logging
console.log(JSON.stringify({
  level: "info",
  timestamp: Date.now(),
  userId,
  action,
}));
```

### Wrangler Tail

View real-time logs during development.

```bash
# Tail logs
wrangler tail

# Filter by status
wrangler tail --status error

# Filter by method
wrangler tail --method POST

# Pretty format
wrangler tail --format pretty
```

### Source Maps

Enable source maps for better error traces.

**tsconfig.json:**

```json
{
  "compilerOptions": {
    "sourceMap": true
  }
}
```

**wrangler.toml:**

```toml
upload_source_maps = true
```

### Local Debugging

Use DevTools for debugging.

```bash
# Start with inspector
wrangler dev --inspector
```

Then open `chrome://inspect` in Chrome and connect to the worker.

### Breakpoints

Set breakpoints in your code.

```typescript
export default {
  async fetch(request: Request): Promise<Response> {
    debugger;  // Execution will pause here

    const data = await fetchData();
    return Response.json(data);
  },
};
```

## Code Organization

### Router Pattern

Organize routes cleanly.

```typescript
interface Route {
  pattern: URLPattern;
  handler: (request: Request, env: Env, params: URLPatternResult) => Promise<Response>;
}

const routes: Route[] = [
  {
    pattern: new URLPattern({ pathname: "/api/users" }),
    handler: handleGetUsers,
  },
  {
    pattern: new URLPattern({ pathname: "/api/users/:id" }),
    handler: handleGetUser,
  },
  {
    pattern: new URLPattern({ pathname: "/api/users" }),
    handler: handleCreateUser,
  },
];

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    for (const route of routes) {
      const match = route.pattern.exec(request.url);
      if (match) {
        return route.handler(request, env, match);
      }
    }

    return Response.json({ error: "Not found" }, { status: 404 });
  },
};

async function handleGetUsers(request: Request, env: Env): Promise<Response> {
  const users = await env.DB.prepare("SELECT * FROM users").all();
  return Response.json(users.results);
}
```

### Middleware Pattern

Chain middleware for cross-cutting concerns.

```typescript
type Middleware = (
  request: Request,
  env: Env,
  next: () => Promise<Response>
) => Promise<Response>;

const corsMiddleware: Middleware = async (request, env, next) => {
  if (request.method === "OPTIONS") {
    return new Response(null, {
      headers: {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE",
      },
    });
  }

  const response = await next();
  response.headers.set("Access-Control-Allow-Origin", "*");
  return response;
};

const authMiddleware: Middleware = async (request, env, next) => {
  const [REDACTED_SECRET]"Authorization");

  if (!token) {
    return Response.json({ error: "Unauthorized" }, { status: 401 });
  }

  // Validate token
  const isValid = await validateToken(token, env);
  if (!isValid) {
    return Response.json({ error: "Invalid token" }, { status: 401 });
  }

  return next();
};

const loggingMiddleware: Middleware = async (request, env, next) => {
  const start = Date.now();
  const response = await next();
  const duration = Date.now() - start;

  console.log({
    method: request.method,
    url: request.url,
    status: response.status,
    duration,
  });

  return response;
};

function applyMiddleware(
  handler: (request: Request, env: Env) => Promise<Response>,
  middlewares: Middleware[]
): (request: Request, env: Env) => Promise<Response> {
  return (request: Request, env: Env) => {
    let index = -1;

    const dispatch = async (i: number): Promise<Response> => {
      if (i <= index) {
        throw new Error("next() called multiple times");
      }

      index = i;

      if (i === middlewares.length) {
        return handler(request, env);
      }

      const middleware = middlewares[i];
      return middleware(request, env, () => dispatch(i + 1));
    };

    return dispatch(0);
  };
}

// Usage
const handler = applyMiddleware(
  async (request, env) => {
    return Response.json({ message: "Hello!" });
  },
  [loggingMiddleware, corsMiddleware, authMiddleware]
);

export default { fetch: handler };
```

### Dependency Injection

Use environment for dependencies.

```typescript
interface Env {
  DB: D1Database;
  CACHE: KVNamespace;
}

class UserService {
  constructor(private env: Env) {}

  async getUser(id: string) {
    // Try cache first
    const cached = await this.env.CACHE.get(`user:${id}`);
    if (cached) return JSON.parse(cached);

    // Fetch from database
    const user = await this.env.DB.prepare(
      "SELECT * FROM users WHERE id = ?"
    ).bind(id).first();

    // Update cache
    if (user) {
      await this.env.CACHE.put(`user:${id}`, JSON.stringify(user), {
        expirationTtl: 3600,
      });
    }

    return user;
  }
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const userService = new UserService(env);
    const user = await userService.getUser("123");
    return Response.json(user);
  },
};
```

### Top-level Env Access

Since March 2025, `import { env } from "cloudflare:workers"` enables accessing bindings outside of handlers. This eliminates prop-drilling `env` through function signatures:

```typescript
import { env } from "cloudflare:workers";

// Module-level initialization using bindings
const config = {
  async getFeatureFlags() {
    return env.CONFIG_KV.get("feature-flags", "json");
  },
};

// Utility functions without env parameter
export async function logEvent(event: string, data: Record<string, unknown>) {
  await env.LOGS_QUEUE.send({ event, data, timestamp: Date.now() });
}
```

This works alongside the traditional `env` parameter approach. Use whichever pattern fits your codebase.

### Runtime Feature Detection

Modern V8 features available in Workers (compat date `2025-09-01`+):

```typescript
// Explicit Resource Management (using keyword)
{
  using handle = getResource();
  // Automatically disposed when scope exits
}

// Uint8Array Base64/Hex encoding
const encoded = new Uint8Array([72, 101, 108, 108, 111]).toBase64(); // "SGVsbG8="
const decoded = Uint8Array.fromBase64("SGVsbG8=");
const hex = new Uint8Array([255, 0]).toHex(); // "ff00"

// MessageChannel / MessagePort for structured communication
const channel = new MessageChannel();
channel.port1.onmessage = (e) => console.log(e.data);
channel.port2.postMessage("hello");
```

## Security Best Practices

### Input Validation

Always validate and sanitize user input.

```typescript
import { z } from "zod";

const userSchema = z.object({
  name: z.string().min(1).max(100),
  email: z.string().email(),
  age: z.number().int().positive().max(150),
});

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    try {
      const body = await request.json();
      const validated = userSchema.parse(body);

      // Use validated data
      await createUser(validated, env);

      return Response.json({ success: true });
    } catch (error) {
      if (error instanceof z.ZodError) {
        return Response.json(
          { error: "Validation failed", issues: error.errors },
          { status: 400 }
        );
      }

      throw error;
    }
  },
};
```

### Rate Limiting

Implement rate limiting to prevent abuse.

```typescript
async function checkRateLimit(
  identifier: string,
  env: Env,
  limit = 100,
  window = 60
): Promise<boolean> {
  const key = `ratelimit:${identifier}`;
  const current = await env.CACHE.get(key);

  if (!current) {
    await env.CACHE.put(key, "1", { expirationTtl: window });
    return true;
  }

  const count = parseInt(current);

  if (count >= limit) {
    return false;
  }

  await env.CACHE.put(key, String(count + 1), { expirationTtl: window });
  return true;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const ip = request.headers.get("CF-Connecting-IP") || "unknown";

    const allowed = await checkRateLimit(ip, env);

    if (!allowed) {
      return Response.json({ error: "Rate limit exceeded" }, { status: 429 });
    }

    return handleRequest(request, env);
  },
};
```

### CSRF Protection

Protect against cross-site request forgery.

```typescript
function generateCSRFToken(): string {
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  return btoa(String.fromCharCode(...array));
}

async function validateCSRFToken(
  [REDACTED_SECRET],
  sessionId: string,
  env: Env
): Promise<boolean> {
  const stored = await env.SESSIONS.get(`csrf:${sessionId}`);
  return stored === token;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    if (request.method === "POST") {
      const sessionId = request.headers.get("X-Session-ID");
      const csrfToken = request.headers.get("X-CSRF-Token");

      if (!sessionId || !csrfToken) {
        return Response.json({ error: "Missing tokens" }, { status: 403 });
      }

      const isValid = await validateCSRFToken(csrfToken, sessionId, env);

      if (!isValid) {
        return Response.json({ error: "Invalid CSRF token" }, { status: 403 });
      }
    }

    return handleRequest(request, env);
  },
};
```

## Additional Resources

- **Testing**: https://developers.cloudflare.com/workers/testing/
- **Observability**: https://developers.cloudflare.com/workers/observability/
- **Best Practices**: https://developers.cloudflare.com/workers/best-practices/
- **Examples**: https://developers.cloudflare.com/workers/examples/


path: references/observability.md
content: # Observability

Monitoring, logging, and debugging Workers in production.

## Logging

### Real-time Logs (Wrangler Tail)

View logs in real-time during development and production.

```bash
# Tail all logs
wrangler tail

# Tail specific environment
wrangler tail --env production

# Filter by status
wrangler tail --status error
wrangler tail --status ok

# Filter by HTTP method
wrangler tail --method POST

# Filter by header
wrangler tail --header "User-Agent: Chrome"

# Filter by IP
wrangler tail --ip 203.0.113.1

# Search in logs
wrangler tail --search "database error"

# Sample rate (% of requests)
wrangler tail --sampling-rate 0.1  # 10%

# Pretty format
wrangler tail --format pretty
```

### Console Logging

Use console methods in your Worker.

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Log levels
    console.log("Info message", { userId: 123 });
    console.info("Info message");
    console.warn("Warning message");
    console.error("Error message", error);
    console.debug("Debug message", { data });

    // Structured logging
    console.log(JSON.stringify({
      level: "info",
      timestamp: Date.now(),
      message: "Request processed",
      userId: 123,
      duration: 45,
    }));

    return new Response("OK");
  },
};
```

### Workers Logs

Persistent logs stored in Cloudflare (Enterprise feature).

**Enable via Dashboard:**
1. Workers & Pages → Logs
2. Enable Workers Logs
3. Set retention period (1-30 days)

**Query logs:**

```bash
# Via GraphQL API
curl -X POST https://api.cloudflare.com/client/v4/graphql \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "query": "query { viewer { accounts(filter: {accountTag: \"$ACCOUNT_ID\"}) { workersLogsData(filter: {datetime_gt: \"2025-09-01T00:00:00Z\"}) { logs { timestamp message } } } } }"
  }'
```

**Filter logs:**

```typescript
// Add custom fields for filtering
console.log(JSON.stringify({
  level: "error",
  service: "api",
  endpoint: "/users",
  userId: "123",
  error: error.message,
}));
```

### Logpush Integration

Stream logs to external services.

**Supported destinations:**
- Amazon S3
- Google Cloud Storage
- Azure Blob Storage
- Datadog
- Splunk
- New Relic
- HTTP endpoint

**Setup via Dashboard:**
1. Logs → Workers Logs → Create Job
2. Select destination
3. Configure filters
4. Set fields to include

**Fields available:**
- `timestamp` - Request timestamp
- `level` - Log level (log, error, warn, info, debug)
- `message` - Log message
- `scriptName` - Worker name
- `outcome` - Request outcome (ok, exception, exceededCpu, etc.)
- `logs` - Array of console.log() messages

### Custom Logging Service

Send logs to your own service.

```typescript
interface LogEntry {
  level: "info" | "warn" | "error";
  message: string;
  timestamp: number;
  metadata?: Record<string, any>;
}

class Logger {
  constructor(private env: Env) {}

  private async send(entry: LogEntry) {
    // Send to logging service
    await fetch("https://logs.example.com/ingest", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${this.env.LOG_TOKEN}`,
      },
      body: JSON.stringify(entry),
    });
  }

  info(message: string, metadata?: Record<string, any>) {
    const entry: LogEntry = {
      level: "info",
      message,
      timestamp: Date.now(),
      metadata,
    };

    console.log(JSON.stringify(entry));
    this.send(entry).catch(console.error);
  }

  error(message: string, error: Error, metadata?: Record<string, any>) {
    const entry: LogEntry = {
      level: "error",
      message,
      timestamp: Date.now(),
      metadata: {
        ...metadata,
        error: error.message,
        stack: error.stack,
      },
    };

    console.error(JSON.stringify(entry));
    this.send(entry).catch(console.error);
  }
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const logger = new Logger(env);

    try {
      logger.info("Request received", {
        url: request.url,
        method: request.method,
      });

      const response = await handleRequest(request, env);

      logger.info("Request completed", {
        status: response.status,
      });

      return response;
    } catch (error) {
      logger.error("Request failed", error as Error, {
        url: request.url,
      });

      throw error;
    }
  },
};
```

## Metrics and Analytics

### Workers Analytics

View request metrics in the dashboard.

**Available metrics:**
- Requests (count, rate)
- Errors (count, rate)
- Success rate
- CPU time (p50, p99)
- Duration (p50, p99)
- Subrequests

**Filter by:**
- Time range
- Status code
- Path
- User agent
- Country

### GraphQL Analytics API

Query analytics programmatically.

```typescript
const query = `
  query {
    viewer {
      accounts(filter: {accountTag: "${accountId}"}) {
        workersInvocationsAdaptive(
          filter: {
            datetime_gt: "2025-09-01T00:00:00Z"
            datetime_lt: "2025-01-02T00:00:00Z"
            scriptName: "my-worker"
          }
          limit: 100
        ) {
          sum {
            requests
            errors
            subrequests
          }
          quantiles {
            cpuTimeP50
            cpuTimeP99
            durationP50
            durationP99
          }
        }
      }
    }
  }
`;

const response = await fetch("https://api.cloudflare.com/client/v4/graphql", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${token}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ query }),
});

const data = await response.json();
```

### Custom Metrics (Analytics Engine)

Write custom metrics to Analytics Engine.

**Configuration:**

```toml
[[analytics_engine_datasets]]
binding = "ANALYTICS"
```

**Write data points:**

```typescript
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const start = Date.now();

    try {
      const response = await handleRequest(request, env);
      const duration = Date.now() - start;

      // Write metrics
      ctx.waitUntil(
        env.ANALYTICS.writeDataPoint({
          // String fields (up to 20)
          blobs: [
            request.url,
            request.method,
            String(response.status),
            request.headers.get("user-agent") || "unknown",
          ],
          // Numeric fields (up to 20)
          doubles: [
            duration,
            response.headers.get("content-length")
              ? parseInt(response.headers.get("content-length")!)
              : 0,
          ],
          // Indexed fields (up to 20) - for filtering
          indexes: [
            request.cf?.country as string || "unknown",
            request.cf?.colo as string || "unknown",
          ],
        })
      );

      return response;
    } catch (error) {
      const duration = Date.now() - start;

      ctx.waitUntil(
        env.ANALYTICS.writeDataPoint({
          blobs: [request.url, request.method, "error"],
          doubles: [duration],
          indexes: ["error"],
        })
      );

      throw error;
    }
  },
};
```

**Query metrics:**

```sql
SELECT
  blob1 AS url,
  blob2 AS method,
  blob3 AS status,
  COUNT() AS requests,
  AVG(double1) AS avg_duration,
  MAX(double1) AS max_duration
FROM ANALYTICS_DATASET
WHERE
  timestamp >= NOW() - INTERVAL '1' DAY
  AND index1 = 'US'
GROUP BY blob1, blob2, blob3
ORDER BY requests DESC
LIMIT 100
```

## Traces (OpenTelemetry)

Export traces to observability platforms.

**Supported platforms:**
- Datadog
- New Relic
- Honeycomb
- Grafana Cloud
- Sentry

### Export to Honeycomb

```typescript
import { trace } from "@opentelemetry/api";
import { WorkersSDK } from "@cloudflare/workers-honeycomb-logger";

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const sdk = new WorkersSDK(request, env, ctx, {
      [REDACTED_SECRET],
      dataset: "my-worker",
    });

    const tracer = trace.getTracer("my-worker");

    return tracer.startActiveSpan("fetch", async (span) => {
      try {
        span.setAttribute("http.method", request.method);
        span.setAttribute("http.url", request.url);

        const response = await handleRequest(request, env);

        span.setAttribute("http.status_code", response.status);
        span.end();

        return response;
      } catch (error) {
        span.recordException(error as Error);
        span.end();
        throw error;
      }
    });
  },
};
```

### Export to Datadog

```typescript
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const startTime = Date.now();

    try {
      const response = await handleRequest(request, env);
      const duration = Date.now() - startTime;

      // Send trace to Datadog
      ctx.waitUntil(
        fetch("https://http-intake.logs.datadoghq.com/v1/input", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "DD-API-KEY": env.DATADOG_API_KEY,
          },
          body: JSON.stringify({
            ddsource: "cloudflare-workers",
            service: "my-worker",
            message: "Request completed",
            duration,
            status: response.status,
            url: request.url,
            method: request.method,
          }),
        })
      );

      return response;
    } catch (error) {
      // Log error to Datadog
      ctx.waitUntil(
        fetch("https://http-intake.logs.datadoghq.com/v1/input", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "DD-API-KEY": env.DATADOG_API_KEY,
          },
          body: JSON.stringify({
            ddsource: "cloudflare-workers",
            service: "my-worker",
            status: "error",
            error: {
              message: (error as Error).message,
              stack: (error as Error).stack,
            },
          }),
        })
      );

      throw error;
    }
  },
};
```

## Error Tracking

### Error Boundaries

Catch and track errors globally.

```typescript
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    try {
      return await handleRequest(request, env, ctx);
    } catch (error) {
      // Log error
      console.error("Unhandled error:", error);

      // Send to error tracking service
      ctx.waitUntil(reportError(error as Error, request, env));

      // Return error response
      return Response.json(
        {
          error: "Internal server error",
          requestId: crypto.randomUUID(),
        },
        { status: 500 }
      );
    }
  },
};

async function reportError(error: Error, request: Request, env: Env) {
  await fetch("https://errors.example.com/report", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `Bearer ${env.ERROR_TOKEN}`,
    },
    body: JSON.stringify({
      error: {
        message: error.message,
        stack: error.stack,
        name: error.name,
      },
      request: {
        url: request.url,
        method: request.method,
        headers: Object.fromEntries(request.headers),
      },
      timestamp: Date.now(),
    }),
  });
}
```

### Sentry Integration

```bash
npm install @sentry/browser
```

```typescript
import * as Sentry from "@sentry/browser";

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    Sentry.init({
      dsn: env.SENTRY_DSN,
      environment: env.ENVIRONMENT,
    });

    try {
      return await handleRequest(request, env);
    } catch (error) {
      Sentry.captureException(error);
      throw error;
    }
  },
};
```

## Performance Monitoring

### Request Timing

Track request performance.

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const timings = {
      start: Date.now(),
      auth: 0,
      database: 0,
      external: 0,
      total: 0,
    };

    // Auth
    const authStart = Date.now();
    const user = await authenticate(request, env);
    timings.auth = Date.now() - authStart;

    // Database
    const dbStart = Date.now();
    const data = await env.DB.prepare("SELECT * FROM users WHERE id = ?")
      .bind(user.id)
      .first();
    timings.database = Date.now() - dbStart;

    // External API
    const apiStart = Date.now();
    const externalData = await fetch("https://api.example.com/data");
    timings.external = Date.now() - apiStart;

    timings.total = Date.now() - timings.start;

    // Log timings
    console.log("Performance:", timings);

    // Add to response headers
    return Response.json(data, {
      headers: {
        "X-Timing-Auth": String(timings.auth),
        "X-Timing-Database": String(timings.database),
        "X-Timing-External": String(timings.external),
        "X-Timing-Total": String(timings.total),
      },
    });
  },
};
```

### Performance API

Use the Performance API for detailed timing.

```typescript
export default {
  async fetch(request: Request): Promise<Response> {
    performance.mark("start");

    performance.mark("db-start");
    await queryDatabase();
    performance.mark("db-end");
    performance.measure("database", "db-start", "db-end");

    performance.mark("api-start");
    await fetchExternal();
    performance.mark("api-end");
    performance.measure("external", "api-start", "api-end");

    performance.mark("end");
    performance.measure("total", "start", "end");

    // Get measurements
    const measurements = performance.getEntriesByType("measure");

    console.log("Performance measurements:", measurements);

    return Response.json({ ok: true });
  },
};
```

## Debugging

### Local Debugging

Debug Workers locally with DevTools.

```bash
# Start with inspector
wrangler dev --inspector

# Connect Chrome DevTools
# Open chrome://inspect in Chrome
# Click "inspect" on your Worker
```

**Features:**
- Set breakpoints
- Step through code
- Inspect variables
- View console logs
- Profile performance

### Remote Debugging

Debug production Workers.

**Using console.log:**

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    console.log("Request:", {
      url: request.url,
      method: request.method,
      headers: Object.fromEntries(request.headers),
    });

    const response = await handleRequest(request, env);

    console.log("Response:", {
      status: response.status,
      headers: Object.fromEntries(response.headers),
    });

    return response;
  },
};
```

**View logs:**

```bash
wrangler tail --format pretty
```

### Source Maps

Enable source maps for better error traces.

**tsconfig.json:**

```json
{
  "compilerOptions": {
    "sourceMap": true
  }
}
```

**wrangler.toml:**

```toml
upload_source_maps = true
```

### Debugging Tips

1. **Use structured logging** - JSON format for easier parsing
2. **Log request IDs** - Track requests across services
3. **Time operations** - Identify performance bottlenecks
4. **Test locally first** - Use `wrangler dev` before deploying
5. **Use staging environment** - Test in production-like environment
6. **Monitor after deploy** - Watch logs and metrics after deployment

## Alerting

### Custom Alerts

Send alerts based on metrics.

```typescript
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    try {
      const response = await handleRequest(request, env);

      // Alert on slow requests
      const duration = Date.now() - startTime;
      if (duration > 5000) {
        ctx.waitUntil(sendAlert("Slow request", { duration, url: request.url }, env));
      }

      return response;
    } catch (error) {
      // Alert on errors
      ctx.waitUntil(sendAlert("Request error", { error: error.message }, env));
      throw error;
    }
  },
};

async function sendAlert(message: string, data: any, env: Env) {
  // Send to Slack
  await fetch(env.SLACK_WEBHOOK, {
    method: "POST",
    body: JSON.stringify({
      text: `🚨 ${message}`,
      blocks: [
        {
          type: "section",
          text: { type: "mrkdwn", text: `*${message}*` },
        },
        {
          type: "section",
          text: { type: "mrkdwn", text: `\`\`\`${JSON.stringify(data, null, 2)}\`\`\`` },
        },
      ],
    }),
  });
}
```

## Health Checks

Implement health check endpoints.

```typescript
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === "/health") {
      return healthCheck(env);
    }

    return handleRequest(request, env);
  },
};

async function healthCheck(env: Env): Promise<Response> {
  const checks = {
    database: false,
    kv: false,
    external: false,
  };

  // Check database
  try {
    await env.DB.prepare("SELECT 1").first();
    checks.database = true;
  } catch (error) {
    console.error("Database check failed:", error);
  }

  // Check KV
  try {
    await env.MY_KV.get("health-check");
    checks.kv = true;
  } catch (error) {
    console.error("KV check failed:", error);
  }

  // Check external API
  try {
    const response = await fetch("https://api.example.com/health", {
      signal: AbortSignal.timeout(2000),
    });
    checks.external = response.ok;
  } catch (error) {
    console.error("External API check failed:", error);
  }

  const allHealthy = Object.values(checks).every((c) => c);

  return Response.json(
    { healthy: allHealthy, checks },
    { status: allHealthy ? 200 : 503 }
  );
}
```

## Additional Resources

- **Observability**: https://developers.cloudflare.com/workers/observability/
- **Logs**: https://developers.cloudflare.com/workers/observability/logs/
- **Metrics**: https://developers.cloudflare.com/workers/observability/metrics-and-analytics/
- **Traces**: https://developers.cloudflare.com/workers/observability/traces/
- **Dev Tools**: https://developers.cloudflare.com/workers/observability/dev-tools/


path: references/wrangler-and-deployment.md
content: # Wrangler and Deployment Guide

Wrangler is the official CLI for developing, testing, and deploying Cloudflare Workers.

## Installation

```bash
# NPM (global)
npm install -g wrangler

# NPM (project-local)
npm install --save-dev wrangler

# Verify installation
wrangler --version
```

## Authentication

```bash
# Login via browser (recommended)
wrangler login

# Or use API token
export CLOUDFLARE_API_TOKEN=your-token
```

## Wrangler v4 Migration

Wrangler v4 shipped March 2025 with breaking changes. If upgrading from v3:

| Change | Migration |
|--------|-----------|
| `kv`/`r2` commands default to **local** | Add `--remote` to CLI commands targeting your Cloudflare account |
| `wrangler generate` removed | Use `npm create cloudflare@latest` |
| `wrangler publish` removed | Use `wrangler deploy` |
| `wrangler pages publish` removed | Use `wrangler pages deploy` |
| `wrangler version` removed | Use `wrangler --version` |
| `legacy_assets` config removed | Migrate to `[assets]` (Workers Static Assets) |
| `node_compat` flag removed | Use `nodejs_compat` compatibility flag |
| `usage_model` config removed | Delete from config (no longer has any effect) |
| `getBindingsProxy()` removed | Use `getPlatformProxy()` (same arguments) |
| Workers Sites deprecated | Migrate to Workers Static Assets |
| Service environments deprecated | Use separate Workers with service bindings |
| Node.js v16 no longer supported | Upgrade to Node.js 20+ |

**esbuild upgrade (v0.17 to v0.24):** Dynamic wildcard imports (e.g., `import('./data/' + kind + '.json')`) now automatically bundle all matching files. Audit existing dynamic imports to avoid unexpected bundling.

**Support timeline:** Wrangler v3 receives critical security fixes only until Q1 2027.

## Essential Commands

### Project Initialization

```bash
# Interactive setup with C3 (recommended)
npm create cloudflare@latest my-worker

# Initialize in current directory
wrangler init
```

### Development

```bash
# Start local dev server
wrangler dev

# Custom port
wrangler dev --port 8080

# With remote resources (bindings)
wrangler dev --remote

# Local mode (no network requests to Cloudflare)
wrangler dev --local

# Test worker (experimental)
wrangler dev --test-scheduled
```

### Deployment

```bash
# Deploy to production
wrangler deploy

# Deploy to specific environment
wrangler deploy --env staging
wrangler deploy --env production

# Dry run (validate without deploying)
wrangler deploy --dry-run

# Deploy specific file
wrangler deploy src/index.ts

# Deploy with message
wrangler deploy --message "Fix authentication bug"
```

### Version Management

```bash
# List versions
wrangler versions list

# View specific version
wrangler versions view <version-id>

# Deploy specific version
wrangler versions deploy <version-id>

# Rollback to previous version
wrangler rollback

# Gradual rollout
wrangler versions deploy <version-id> --percentage 10
```

## Configuration (wrangler.toml)

### Basic Structure

```toml
#:schema node_modules/wrangler/config-schema.json
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2025-09-01"

# Account/Zone (usually auto-detected)
account_id = "your-account-id"

# Workers.dev subdomain
workers_dev = true

# Or custom domain
routes = [
  { pattern = "example.com/*", zone_name = "example.com" },
  { pattern = "api.example.com/*", zone_name = "example.com" }
]
```

### Environment Variables

```toml
# Non-sensitive variables
[vars]
ENVIRONMENT = "production"
API_ENDPOINT = "https://api.example.com"
FEATURE_FLAGS = '{"newUI": true}'

# Per-environment
[env.staging.vars]
ENVIRONMENT = "staging"
API_ENDPOINT = "https://staging-api.example.com"
```

### Secrets

```bash
# Set secret via CLI (not in wrangler.toml!)
wrangler secret put API_KEY
# Enter value when prompted

# List secrets
wrangler secret list

# Delete secret
wrangler secret delete API_KEY

# Bulk import from .env
wrangler secret bulk .env.production
```

### Bindings Configuration

**KV:**
```toml
[[kv_namespaces]]
binding = "MY_KV"
id = "your-namespace-id"
preview_id = "preview-namespace-id"
```

**D1:**
```toml
[[d1_databases]]
binding = "DB"
database_name = "production-db"
database_id = "xxxx-xxxx-xxxx"
```

**R2:**
```toml
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "my-bucket"
preview_bucket_name = "my-bucket-preview"
```

**Durable Objects:**
```toml
[[durable_objects.bindings]]
name = "COUNTER"
class_name = "Counter"
script_name = "my-worker"

[[migrations]]
tag = "v1"
new_classes = ["Counter"]
```

**Queues:**
```toml
[[queues.producers]]
binding = "MY_QUEUE"
queue = "my-queue"

[[queues.consumers]]
queue = "my-queue"
max_batch_size = 10
max_batch_timeout = 30
max_retries = 3
dead_letter_queue = "my-dlq"
```

**Service Bindings:**
```toml
[[services]]
binding = "AUTH_SERVICE"
service = "auth-worker"
environment = "production"
```

**Workers AI:**
```toml
[ai]
binding = "AI"
```

### Cron Triggers

```toml
[triggers]
crons = [
  "0 0 * * *",     # Daily at midnight
  "*/15 * * * *",  # Every 15 minutes
  "0 9 * * 1-5"    # Weekdays at 9 AM
]
```

### Static Assets

```toml
[assets]
directory = "./public"
binding = "ASSETS"

# HTML handling
html_handling = "auto-trailing-slash"  # or "drop-trailing-slash", "none"

# Not found handling
not_found_handling = "single-page-application"  # or "404-page", "none"
```

### Compatibility

```toml
# Compatibility date (required)
compatibility_date = "2025-09-01"

# Compatibility flags
compatibility_flags = [
  "nodejs_compat",
  "nodejs_compat_populate_process_env"  # Auto-populate process.env with text bindings
]
# With compat date 2025-09-01+, node:fs is enabled by default under nodejs_compat.
# Use "enable_nodejs_fs_module" flag to enable it on earlier compat dates.
```

### Custom Builds

```toml
[build]
command = "npm run build"
watch_dirs = ["src", "public"]

[build.upload]
format = "modules"
dir = "dist"
main = "./index.js"
```

## Multi-Environment Setup

### Environment Structure

```toml
# Global settings
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2025-09-01"

# Default/production
[vars]
ENVIRONMENT = "production"

[[kv_namespaces]]
binding = "CACHE"
id = "prod-kv-id"

# Staging environment
[env.staging]
name = "my-worker-staging"
vars = { ENVIRONMENT = "staging" }

[[env.staging.kv_namespaces]]
binding = "CACHE"
id = "staging-kv-id"

# Development environment
[env.dev]
name = "my-worker-dev"
vars = { ENVIRONMENT = "development" }

[[env.dev.kv_namespaces]]
binding = "CACHE"
id = "dev-kv-id"
```

### Deploying Environments

```bash
# Deploy to production (default)
wrangler deploy

# Deploy to staging
wrangler deploy --env staging

# Deploy to dev
wrangler deploy --env dev
```

## CI/CD Integration

### GitHub Actions

**.github/workflows/deploy.yml:**

```yaml
name: Deploy Worker

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    name: Deploy
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Deploy to Cloudflare Workers
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
```

**Multi-environment:**

```yaml
name: Deploy Workers

on:
  push:
    branches: [main, staging]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Deploy to staging
        if: github.ref == 'refs/heads/staging'
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          environment: 'staging'

      - name: Deploy to production
        if: github.ref == 'refs/heads/main'
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
```

### GitLab CI/CD

**.gitlab-ci.yml:**

```yaml
stages:
  - deploy

deploy_production:
  stage: deploy
  image: node:20
  script:
    - npm ci
    - npx wrangler deploy
  only:
    - main
  variables:
    CLOUDFLARE_API_TOKEN: $CLOUDFLARE_API_TOKEN
    CLOUDFLARE_ACCOUNT_ID: $CLOUDFLARE_ACCOUNT_ID
```

## Workers Builds (Git Integration)

Enable automatic deployments on git push via the dashboard.

**Setup:**
1. Connect your GitHub/GitLab repository
2. Configure build settings
3. Set environment variables
4. Enable branch deployments

**Benefits:**
- Automatic builds on push
- Preview deployments for PRs
- Build caching
- Deployment history

## Versioning & Gradual Deployments

### Versions

Workers automatically create versions on each deployment.

```bash
# List versions
wrangler versions list

# View specific version
wrangler versions view <version-id>

# Deploy specific version
wrangler versions deploy <version-id>
```

### Gradual Rollouts

Incrementally deploy new versions to reduce risk.

**Via Wrangler:**

```bash
# Deploy to 10% of traffic
wrangler versions deploy <version-id> --percentage 10

# Increase to 50%
wrangler versions deploy <version-id> --percentage 50

# Full rollout
wrangler versions deploy <version-id> --percentage 100
```

**Via Configuration:**

```toml
[[workflows.deployments]]
version_id = "new-version-id"
percentage = 10

[[workflows.deployments]]
version_id = "old-version-id"
percentage = 90
```

### Rollback

```bash
# Rollback to previous version
wrangler rollback

# List rollback history
wrangler rollback --list

# Rollback to specific version
wrangler versions deploy <previous-version-id>
```

## Resource Management

### KV Namespaces

**Wrangler v4:** `kv` and `r2` commands default to local storage. Add `--remote` to operate on your Cloudflare account.

```bash
# Create namespace
wrangler kv:namespace create "MY_KV"

# List namespaces
wrangler kv:namespace list

# Delete namespace
wrangler kv:namespace delete --namespace-id=<id>

# Put key-value (--remote for production data)
wrangler kv:key put "key" "value" --namespace-id=<id> --remote

# Get value
wrangler kv:key get "key" --namespace-id=<id> --remote

# List keys
wrangler kv:key list --namespace-id=<id> --remote

# Delete key
wrangler kv:key delete "key" --namespace-id=<id> --remote

# Bulk operations
wrangler kv:bulk put data.json --namespace-id=<id> --remote
wrangler kv:bulk delete keys.json --namespace-id=<id> --remote
```

### D1 Databases

```bash
# Create database
wrangler d1 create my-database

# List databases
wrangler d1 list

# Execute SQL
wrangler d1 execute my-database --command="SELECT * FROM users"

# Execute from file
wrangler d1 execute my-database --file=schema.sql

# Migrations
wrangler d1 migrations create my-database "add-users-table"
wrangler d1 migrations apply my-database
wrangler d1 migrations list my-database

# Export database
wrangler d1 export my-database --output=backup.sql

# Time Travel (restore)
wrangler d1 time-travel restore my-database --timestamp=<timestamp>
```

### R2 Buckets

```bash
# Create bucket
wrangler r2 bucket create my-bucket

# List buckets
wrangler r2 bucket list

# Delete bucket
wrangler r2 bucket delete my-bucket

# Put object (--remote for production data in Wrangler v4)
wrangler r2 object put my-bucket/file.txt --file=./file.txt --remote

# Get object
wrangler r2 object get my-bucket/file.txt --file=./downloaded.txt --remote

# List objects
wrangler r2 object list my-bucket --remote

# Delete object
wrangler r2 object delete my-bucket/file.txt --remote
```

### Queues

```bash
# Create queue
wrangler queues create my-queue

# List queues
wrangler queues list

# Delete queue
wrangler queues delete my-queue

# Send test message
wrangler queues send my-queue '{"test": "message"}'
```

## Remote Bindings for Local Dev

Connect local `wrangler dev` to real production bindings (GA September 2025). Replaces the removed `getBindingsProxy()` API.

```bash
# Run local dev with remote bindings
wrangler dev --remote
```

This connects to your actual KV, D1, R2, and other bindings on Cloudflare, while still running your Worker code locally. Useful for debugging against production data without deploying.

## Debugging & Troubleshooting

### Tail Logs (Real-time)

```bash
# Tail logs from production
wrangler tail

# Tail specific environment
wrangler tail --env staging

# Filter by status
wrangler tail --status error

# Filter by method
wrangler tail --method POST

# Pretty print
wrangler tail --format pretty
```

### Deployment Issues

**Version conflicts:**
```bash
# Force overwrite
wrangler deploy --force
```

**Bundle size issues:**
```bash
# Check bundle size
wrangler deploy --dry-run --outdir=dist

# Optimize
npm run build -- --minify
```

**Authentication issues:**
```bash
# Re-login
wrangler login

# Use API token
export CLOUDFLARE_API_TOKEN=your-token
```

## Best Practices

### Configuration Management

1. **Use environments** for staging/production
2. **Store secrets in Wrangler**, not in config files
3. **Use compatibility dates** to lock runtime behavior
4. **Version control** your wrangler.toml

### Deployment Strategy

1. **Test locally** with `wrangler dev`
2. **Deploy to staging** first
3. **Use gradual rollouts** for production
4. **Monitor logs** during deployment
5. **Keep previous versions** for quick rollback

### CI/CD Best Practices

1. **Separate staging and production** workflows
2. **Use deployment keys** with minimal permissions
3. **Run tests** before deployment
4. **Tag releases** in git
5. **Notify team** on deployments

### Performance Optimization

1. **Minimize bundle size** - Tree-shake unused code
2. **Use custom builds** for complex projects
3. **Enable build caching** in CI/CD
4. **Optimize dependencies** - Use smaller packages

## Advanced Features

### Custom Domains

```toml
routes = [
  { pattern = "api.example.com/*", zone_name = "example.com", custom_domain = true }
]
```

```bash
# Add custom domain via CLI
wrangler domains add api.example.com
```

### Workers for Platforms

Deploy user-provided Workers on your infrastructure.

```toml
[[dispatch_namespaces]]
binding = "DISPATCHER"
namespace = "my-namespace"
outbound = { service = "my-worker" }
```

### Smart Placement

Automatically place Workers near data sources.

```toml
[placement]
mode = "smart"
```

## Additional Resources

- **Wrangler Docs**: https://developers.cloudflare.com/workers/wrangler/
- **Configuration**: https://developers.cloudflare.com/workers/wrangler/configuration/
- **Commands**: https://developers.cloudflare.com/workers/wrangler/commands/
- **CI/CD**: https://developers.cloudflare.com/workers/ci-cd/
- **GitHub Actions**: https://github.com/cloudflare/wrangler-action


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)