Skip to content
Back to skills

Infra Platform Cloudflare Workers

ASecurity

Cloudflare Workers edge compute platform — Wrangler CLI, KV, D1, R2, Durable Objects, Queues, Workers AI

  • 24 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added May 29, 2026
ai-agentsjavascripttypescriptgojavasqlnodenodejstestinggitapi

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 9 files and shows the line behind each finding

Scanned May 29, 2026

npx -y skills add agents-inc/skills --skill infra-platform-cloudflare-workers --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Infra Platform Cloudflare Workers?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Infra Platform Cloudflare Workers
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/agents-inc-infra-platform-cloudflare-workers/badge)](https://www.skillsdirectory.com/skills/agents-inc-infra-platform-cloudflare-workers)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: infra-platform-cloudflare-workers
description: Cloudflare Workers edge compute platform — Wrangler CLI, KV, D1, R2, Durable Objects, Queues, Workers AI
---

# Cloudflare Workers Patterns

> **Quick Guide:** Cloudflare Workers run TypeScript/JavaScript on Cloudflare's global edge network with V8 isolates (not containers). Use `wrangler.jsonc` for configuration, `wrangler dev` for local development, and `wrangler deploy` for production. Access KV, D1, R2, Queues, Durable Objects, and Workers AI through type-safe bindings on the `env` parameter. Run `wrangler types` to auto-generate your `Env` interface. Stream large payloads — Workers have a 128 MB memory limit. Never store request-scoped state in module-level variables.

---

<critical_requirements>

## CRITICAL: Before Using This Skill

> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)

**(You MUST run `wrangler types` to generate your Env interface — NEVER hand-write binding types)**

**(You MUST use `wrangler.jsonc` for new projects — Cloudflare recommends JSON config and some features are JSON-only)**

**(You MUST stream large request/response bodies — NEVER buffer entire payloads in memory (128 MB limit))**

**(You MUST avoid module-level mutable state — Workers reuse V8 isolates across requests, causing cross-request data leaks)**

**(You MUST use bindings for Cloudflare services (KV, D1, R2, Queues) — NEVER use REST APIs from within Workers)**

</critical_requirements>

---

## Examples

- [Core Setup & Configuration](examples/core.md) — wrangler.jsonc, project init, fetch handler, secrets, multi-env, CI/CD, testing
- [KV Storage](examples/kv.md) — KV binding, typed get/put, TTL, stale-while-revalidate caching
- [D1 Database](examples/d1.md) — D1 binding, parameterized queries, batch ops, migrations, CRUD API
- [R2 Object Storage](examples/r2.md) — R2 binding, file upload/download/delete with streaming
- [Durable Objects](examples/durable-objects.md) — DO classes, SQLite, RPC, rate limiter, WebSocket chat
- [Routing & Middleware](examples/routing.md) — API framework integration, middleware, queues, cron, service bindings, AI, streaming
- [Quick Reference](reference.md) — Wrangler CLI commands, binding type signatures, config template, CPU limits

---

**Auto-detection:** Cloudflare Workers, wrangler, wrangler.toml, wrangler.jsonc, Workers KV, Cloudflare KV, D1 database, R2 bucket, Durable Objects, Cloudflare Queues, Workers AI, service binding, miniflare, compatibility_date, compatibility_flags, nodejs_compat, cloudflare:workers, ExportedHandler, DurableObject, wrangler dev, wrangler deploy, wrangler types, Cloudflare Pages Functions, edge worker, CF Worker

**When to use:**

- Deploying TypeScript/JavaScript to Cloudflare's edge network
- Configuring Wrangler CLI for local development and deployment
- Using Cloudflare bindings: KV, D1, R2, Queues, Durable Objects, Workers AI
- Building APIs on Workers with a framework (e.g., Hono)
- Implementing real-time features with Durable Objects and WebSockets
- Setting up cron triggers and scheduled handlers
- Configuring service bindings for worker-to-worker communication
- Managing environment variables, secrets, and multi-environment deploys

**When NOT to use:**

- Long-running compute tasks exceeding CPU time limits (use traditional servers or Workflows)
- Applications requiring persistent TCP connections to external databases without Hyperdrive
- Workloads needing more than 128 MB memory per request

**Key patterns covered:**

- Wrangler configuration (`wrangler.jsonc`) and project setup
- Fetch handler, scheduled handler, and queue handler
- KV key-value storage (caching, config, session data)
- D1 SQLite database (relational data at the edge)
- R2 S3-compatible object storage (files, uploads, assets)
- Durable Objects (stateful edge compute, WebSockets, coordination)
- Cloudflare Queues (async message processing)
- Workers AI (inference at the edge)
- Framework integration (Hono examples) with typed bindings
- Environment variables, secrets, and multi-environment config
- Service bindings (worker-to-worker RPC)
- Cron triggers and scheduled workers
- Streaming and performance optimization
- Testing with Workers-native test pool

---

<philosophy>

## Philosophy

Cloudflare Workers run on V8 isolates (not containers) across 300+ data centers worldwide. They start in under 5ms with zero cold starts. The programming model is fundamentally different from traditional servers:

1. **Bindings over APIs** - Access Cloudflare services (KV, D1, R2, Queues) through direct in-process bindings on the `env` parameter, not REST API calls. Bindings have zero network hop and zero auth overhead.
2. **Stateless by default** - Each request gets a fresh execution context. Workers reuse V8 isolates, so module-level variables persist across requests — this is a bug source, not a feature.
3. **Stream everything** - Workers have a 128 MB memory limit. Buffer nothing; stream request and response bodies using `TransformStream` and `pipeTo`.
4. **Edge-first architecture** - Code runs closest to the user. Use Durable Objects when you need coordination or state; use D1/KV/R2 for persistence.

**When to use Workers:**

- API endpoints, middleware, and request routing
- Caching layers and content transformation
- Webhook receivers and event processors
- Real-time collaboration (with Durable Objects)
- Full-stack applications (with Workers Static Assets or Pages)

**When NOT to use Workers:**

- CPU-intensive compute exceeding limits (10ms free / 30s paid per request)
- Workloads requiring more than 128 MB memory
- Applications needing persistent database connections (use Hyperdrive as a proxy)
- Long-running background jobs exceeding limits (use Workflows for durable execution)

</philosophy>

---

<patterns>

## Core Patterns

### Pattern 1: Project Setup and Wrangler Configuration

Every Workers project starts with `wrangler.jsonc`. Cloudflare recommends JSON format — some newer features are JSON-only. Run `wrangler types` after changing bindings to regenerate the `Env` interface.

```jsonc
// wrangler.jsonc
{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "name": "my-api",
  "main": "src/index.ts",
  "compatibility_date": "2025-09-15",
  "compatibility_flags": ["nodejs_compat"],
  "observability": { "enabled": true },
  "placement": { "mode": "smart" },
  "upload_source_maps": true,
}
```

See [examples/core.md](examples/core.md) for full project initialization, multi-environment config, secrets management, CI/CD, and testing setup.

---

### Pattern 2: Fetch Handler (Request/Response)

The fetch handler is the entry point for HTTP requests. Use `satisfies ExportedHandler<Env>` for type safety.

```typescript
import type { ExportedHandler } from "cloudflare:workers";

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

Never store mutable state in module-level variables — V8 isolate reuse causes cross-request data leaks. See [examples/core.md](examples/core.md) for complete handler with CORS and routing.

---

### Pattern 3: KV Key-Value Storage

KV is an eventually-consistent key-value store for read-heavy workloads. Use typed `get<T>(key, "json")`, always set `expirationTtl`, and use `ctx.waitUntil()` for non-blocking writes.

```typescript
const CACHE_TTL_SECONDS = 3_600;
const profile = await env.CACHE.get<UserProfile>(`user:${id}`, "json");
ctx.waitUntil(
  env.CACHE.put(`user:${id}`, JSON.stringify(data), {
    expirationTtl: CACHE_TTL_SECONDS,
  }),
);
```

**When to use:** Read-heavy workloads (config, cache, feature flags) where eventual consistency is acceptable (~60s propagation).

**When not to use:** Relational data (use D1), frequent writes to same key, strong consistency (use Durable Objects).

See [examples/kv.md](examples/kv.md) for stale-while-revalidate pattern and full caching examples.

---

### Pattern 4: D1 SQLite Database

D1 is serverless SQLite at the edge. Always use parameterized queries via `prepare().bind()` to prevent SQL injection. Use `batch()` for atomic multi-statement operations. Use `withSession()` for read replica consistency when read replication is enabled.

```typescript
const user = await db
  .prepare("SELECT * FROM users WHERE email = ?")
  .bind(email)
  .first<User>();

const { results } = await db
  .prepare("SELECT * FROM users LIMIT ? OFFSET ?")
  .bind(DEFAULT_PAGE_SIZE, offset)
  .all<User>();
```

See [examples/d1.md](examples/d1.md) for migrations, batch operations, and full CRUD API.

---

### Pattern 5: R2 Object Storage

R2 is S3-compatible storage with zero egress fees. Always stream R2 bodies directly to responses — never call `.arrayBuffer()` or `.text()` on large objects.

```typescript
const object = await env.BUCKET.get(key);
if (!object) return new Response("Not Found", { status: 404 });
const headers = new Headers();
object.writeHttpMetadata(headers);
return new Response(object.body, { headers }); // Stream directly
```

See [examples/r2.md](examples/r2.md) for file service with content-type validation and list operations.

---

### Pattern 6: Durable Objects (Stateful Edge Compute)

Durable Objects provide single-threaded, strongly consistent compute. Each instance has SQLite storage. Use RPC methods (not fetch) and `blockConcurrencyWhile` for schema migrations.

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

export class Counter extends DurableObject<Env> {
  constructor(ctx: DurableObjectState, env: Env) {
    super(ctx, env);
    ctx.blockConcurrencyWhile(async () => {
      this.ctx.storage.sql.exec("CREATE TABLE IF NOT EXISTS ...");
    });
  }
  async increment(name: string): Promise<number> {
    /* RPC method */
  }
}
```

**When to use:** Coordination (chat, collaboration), per-entity state (sessions, game instances), WebSocket connections, rate limiting.

**When not to use:** Stateless requests, high fan-out, global rate limiting (bottleneck).

See [examples/durable-objects.md](examples/durable-objects.md) for rate limiter and WebSocket chat with hibernation.

---

### Pattern 7: Queues, Cron, Service Bindings, Workers AI

**Queues** decouple producers from consumers with at-least-once delivery and configurable retries. **Cron triggers** invoke Workers on a schedule. **Service bindings** enable zero-cost worker-to-worker calls. **Workers AI** runs inference on Cloudflare's GPU network.

See [examples/routing.md](examples/routing.md) for all these patterns with full configuration and code examples.

---

### Pattern 8: API Framework Integration

For structured APIs, use a routing framework with typed bindings. Export the framework's `fetch` handler alongside scheduled/queue handlers.

```typescript
import { Hono } from "hono";
const app = new Hono<{ Bindings: Env }>();

app.get("/users/:id", async (c) => {
  const user = await c.env.DB.prepare("SELECT * FROM users WHERE id = ?")
    .bind(c.req.param("id"))
    .first();
  return user ? c.json(user) : c.json({ error: "Not found" }, 404);
});

export default app;
```

See [examples/routing.md](examples/routing.md) for production API with middleware, error handling, and multi-handler setup.

</patterns>

---

<performance>

## Performance Optimization

### Request Processing

| Technique                             | Impact                                                   |
| ------------------------------------- | -------------------------------------------------------- |
| Smart Placement (`"mode": "smart"`)   | Routes to optimal data center based on binding locations |
| Streaming responses                   | Avoids 128 MB memory limit, improves TTFB                |
| `ctx.waitUntil()` for background work | Returns response immediately, processes async work after |
| `nodejs_compat` flag                  | Access Node.js built-in modules (crypto, buffer, stream) |

### Storage Performance

| Storage         | Reads              | Writes                  | Consistency              | Best For                 |
| --------------- | ------------------ | ----------------------- | ------------------------ | ------------------------ |
| KV              | Fast (edge cached) | Slow (~60s propagation) | Eventually consistent    | Cache, config, flags     |
| D1              | Medium             | Medium                  | Strong (per-region)      | Relational data, queries |
| R2              | Medium             | Medium                  | Strong                   | Files, blobs, uploads    |
| Durable Objects | Fast (in-memory)   | Fast (SQLite)           | Strong (single-threaded) | Coordination, real-time  |

### Hyperdrive for External Databases

When connecting to PostgreSQL/MySQL outside Cloudflare, always use Hyperdrive. It maintains a connection pool close to your database, eliminating per-request TCP/TLS overhead.

### CPU Time Limits

| Plan          | CPU Time per Request                                |
| ------------- | --------------------------------------------------- |
| Free          | 10 ms                                               |
| Paid          | 30 seconds (default), configurable up to 15 minutes |
| Cron Triggers | 15 minutes                                          |

</performance>

---

<decision_framework>

## Decision Framework

### Choosing a Storage Primitive

```
What kind of data?
  |
  +-- Key-value pairs (cache, config, sessions)
  |     +-- Read-heavy, eventual consistency OK --> KV
  |     +-- Strong consistency needed --> Durable Objects
  |
  +-- Relational data with queries
  |     +-- Edge-native SQLite --> D1
  |     +-- External PostgreSQL/MySQL --> Hyperdrive
  |
  +-- Files and blobs (images, documents)
  |     +-- S3-compatible storage --> R2
  |
  +-- Coordination state (chat, multiplayer, collaboration)
  |     +-- Single-threaded consistency --> Durable Objects
  |
  +-- Message passing / background work
        +-- Simple fan-out, buffering --> Queues
        +-- Multi-step durable execution --> Workflows
```

### Choosing Between Workers and Pages

```
What are you building?
  |
  +-- API only (no frontend) --> Workers
  |
  +-- Static site + API --> Pages with Functions
  |
  +-- Full-stack with SSR --> Pages (framework) or Workers + Static Assets
  |
  +-- Background processing / cron --> Workers (Pages lacks cron support)
  |
  +-- Real-time / WebSockets --> Workers + Durable Objects
```

### When to Use Durable Objects vs D1

```
Do you need coordination between concurrent requests?
  |
  +-- YES (chat, game, collaboration) --> Durable Objects
  |     +-- Single-threaded, no race conditions
  |     +-- WebSocket support with hibernation
  |     +-- Per-entity sharding (one DO per room/session)
  |
  +-- NO (CRUD, reporting, querying) --> D1
        +-- Full SQL support
        +-- Cross-entity queries
        +-- Traditional database patterns
```

</decision_framework>

---

<integration>

## Integration Guide

**Cloudflare Platform Services:**

- **Hyperdrive**: Connection pooling proxy for external PostgreSQL/MySQL — eliminates per-request TCP/TLS overhead
- **Vectorize**: Vector database for embeddings and semantic search with Workers AI
- **Cloudflare Pages**: Static site hosting with Workers-powered functions for full-stack apps
- **GitHub Actions**: `cloudflare/wrangler-action@v3` for CI/CD deployment

**Framework & ORM Compatibility:**

Workers are compatible with edge-optimized frameworks and ORMs that support the V8 runtime. Any framework that exports a `fetch` handler works (Hono, itty-router, etc.). D1 works with any ORM that supports SQLite (check your ORM's Workers compatibility docs).

**Testing:**

Use `@cloudflare/vitest-pool-workers` to run tests inside the actual Workers runtime with real bindings (KV, D1, R2). See [examples/core.md](examples/core.md) for test configuration.

</integration>

---

<red_flags>

## RED FLAGS

**High Priority Issues:**

- Storing request-scoped data in module-level variables (V8 isolate reuse causes cross-request leaks)
- Buffering entire request/response bodies with `.text()` / `.arrayBuffer()` (128 MB memory limit)
- Hand-writing `Env` interface instead of running `wrangler types` (mismatches between config and code)
- Using REST APIs for KV/D1/R2/Queues from within Workers instead of bindings (unnecessary latency and auth overhead)
- Putting secrets in `wrangler.jsonc`, source code, or environment variables (use `wrangler secret put`)
- Creating a single global Durable Object for all traffic (bottleneck at ~1000 req/sec)

**Medium Priority Issues:**

- Using `wrangler.toml` for new projects (JSON format recommended, some features JSON-only)
- Outdated `compatibility_date` (misses runtime improvements and bug fixes)
- Missing `observability` config (production Workers are a black box without logs/traces)
- Destructuring `ctx` in fetch handler (loses `this` binding, `ctx.waitUntil` throws "Illegal invocation")
- Using `exec()` for D1 queries instead of `prepare().bind()` (no parameterization, SQL injection risk)
- Not reciprocating WebSocket close in Durable Objects (causes 1006 errors)

**Common Mistakes:**

- Forgetting that KV is eventually consistent (~60s propagation) and expecting instant reads after writes
- Using `Math.random()` for security-sensitive tokens (use `crypto.randomUUID()` or `crypto.getRandomValues()`)
- Not using `ctx.waitUntil()` for post-response background work (work may be cancelled when response is sent)
- Comparing secrets with `===` instead of `crypto.subtle.timingSafeEqual()` (timing side-channel attack)
- Using `passThroughOnException()` as error handling (hides bugs, use explicit try/catch)
- Floating promises (not awaited, not returned, not passed to `waitUntil()`) causing silent failures — enable `@typescript-eslint/no-floating-promises` to catch at dev time

**Gotchas and Edge Cases:**

- Bindings (KV, D1, R2, etc.) are NOT inherited across Wrangler environments — you must re-declare them per environment
- `wrangler dev` uses local simulation by default; use `--remote` to test against real Cloudflare services
- D1 batch operations execute sequentially (not in parallel) but atomically
- Durable Objects in-memory state is lost on eviction — always persist important data to SQLite first
- Unnecessary `await` between DO storage writes breaks write coalescing — batch writes happen atomically when you don't await between them
- DO alarm handlers may fire multiple times — design them to be idempotent
- Using `blockConcurrencyWhile()` on every request limits throughput to ~200 req/sec — use it only for initialization
- Workers on the free plan have a 10ms CPU time limit per request (not wall-clock time — I/O waiting is free)
- Cron trigger changes take up to 15 minutes to propagate globally
- `.dev.vars` file is for local secrets only and must be gitignored
- The `env` parameter is provided per-request by the runtime; avoid caching binding references or derived objects at module scope

</red_flags>

---

<critical_reminders>

## CRITICAL REMINDERS

> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)

**(You MUST run `wrangler types` to generate your Env interface — NEVER hand-write binding types)**

**(You MUST use `wrangler.jsonc` for new projects — Cloudflare recommends JSON config and some features are JSON-only)**

**(You MUST stream large request/response bodies — NEVER buffer entire payloads in memory (128 MB limit))**

**(You MUST avoid module-level mutable state — Workers reuse V8 isolates across requests, causing cross-request data leaks)**

**(You MUST use bindings for Cloudflare services (KV, D1, R2, Queues) — NEVER use REST APIs from within Workers)**

**Failure to follow these rules will result in memory crashes (buffering), data leaks (module state), type mismatches (hand-written Env), unnecessary latency (REST over bindings), and secret exposure (secrets in config).**

</critical_reminders>

Files in this skill

  • SKILL.md19.9 KB
  • examples/cloudflare-workers.md889 B
  • examples/core.md8.8 KB
  • examples/d1.md6.6 KB
  • examples/durable-objects.md8.9 KB
  • examples/kv.md5.4 KB
  • examples/r2.md5.2 KB
  • examples/routing.md13 KB
  • reference.md10.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.