Claude
Skills
Sign in
Back

workers

Included with Lifetime
$97 forever

Core Workers fundamentals including handlers, configuration, and Service Bindings. Load when creating new Workers, configuring wrangler.jsonc, implementing fetch/scheduled/queue handlers, using Service Bindings for RPC, generating types with wrangler types, or building microservices.

General

What this skill does


# Cloudflare Workers

Essential patterns for building Cloudflare Workers applications with TypeScript, proper configuration, and Service Bindings for microservices.

## FIRST: Project Setup

Initialize a new Workers project:

```bash
npm create cloudflare@latest my-worker
# OR
wrangler init my-worker
```

**Minimal wrangler.jsonc:**

```jsonc
{
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_date": "2025-03-07",
  "compatibility_flags": ["nodejs_compat"],
  "observability": {
    "enabled": true,
    "head_sampling_rate": 1
  }
}
```

## Code Standards

| Standard | Requirement | Notes |
|----------|-------------|-------|
| **Language** | TypeScript by default | JavaScript only if explicitly requested |
| **Module Format** | ES modules only | NEVER use Service Worker format |
| **Imports** | Always import types/classes | Must import all used methods |
| **File Structure** | Single file unless specified | Keep code in one file by default |
| **Dependencies** | Minimize external deps | Use official SDKs when available |
| **Native Bindings** | Not supported | Avoid FFI/C bindings |
| **Types** | Include TypeScript types | Define Env interface for bindings |

## Handler Patterns

### HTTP Request Handler (fetch)

```typescript
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(request.url);
    
    // Route handling
    if (url.pathname === "/api/data") {
      return handleAPI(request, env);
    }
    
    return new Response("Hello World!", {
      headers: { "Content-Type": "text/plain" }
    });
  }
};

async function handleAPI(request: Request, env: Env): Promise<Response> {
  // Validate request method
  if (request.method !== "POST") {
    return new Response("Method not allowed", { status: 405 });
  }
  
  try {
    const data = await request.json();
    // Process data...
    return Response.json({ success: true, data });
  } catch (error) {
    return Response.json(
      { error: "Invalid JSON" },
      { status: 400 }
    );
  }
}
```

### Scheduled Handler (cron)

```typescript
export default {
  async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise<void> {
    // Run scheduled tasks
    console.log("Cron triggered:", new Date(event.scheduledTime).toISOString());
    
    // Use waitUntil for background work
    ctx.waitUntil(performCleanup(env));
  }
};

async function performCleanup(env: Env): Promise<void> {
  // Background cleanup logic
  console.log("Cleanup completed");
}
```

**wrangler.jsonc configuration:**

```jsonc
{
  "triggers": {
    "crons": ["0 */6 * * *"]  // Every 6 hours
  }
}
```

### Queue Consumer Handler

```typescript
export default {
  async queue(batch: MessageBatch<QueueMessage>, env: Env, ctx: ExecutionContext): Promise<void> {
    for (const message of batch.messages) {
      try {
        await processMessage(message.body, env);
        message.ack();
      } catch (error) {
        console.error("Message processing failed:", error);
        message.retry();
      }
    }
  }
};

type QueueMessage = {
  id: string;
  data: unknown;
};

async function processMessage(body: QueueMessage, env: Env): Promise<void> {
  // Process queue message
  console.log("Processing message:", body.id);
}
```

## Auto-Generate Environment Types

**RECOMMENDED:** Use `wrangler types` to automatically generate your `Env` interface from your `wrangler.jsonc`:

```bash
# Generate types from wrangler.jsonc
npx wrangler types

# Output to custom path
npx wrangler types ./types/env.d.ts

# Include runtime types (Wrangler >= 3.66.0)
npx wrangler types --experimental-include-runtime
```

This generates a `worker-configuration.d.ts` file with:
- **Env interface** matching all your bindings (KV, R2, D1, secrets, etc.)
- **Runtime types** matching your `compatibility_date` and `compatibility_flags`
- **Service binding types** with full RPC method signatures

**Add to tsconfig.json:**
```jsonc
{
  "compilerOptions": {
    "types": ["@cloudflare/workers-types", "./worker-configuration"]
  }
}
```

**When to regenerate:**
- After adding/removing bindings in wrangler.jsonc
- After changing compatibility_date or compatibility_flags
- After modifying .dev.vars (secrets)
- Before deploying (run in CI/CD)

**Example generated Env interface:**

```typescript
// worker-configuration.d.ts (auto-generated)
interface Env {
  // From wrangler.jsonc bindings
  MY_KV: KVNamespace;
  MY_BUCKET: R2Bucket;
  DB: D1Database;
  COUNTER: DurableObjectNamespace;
  AUTH_SERVICE: Service<typeof AuthService>;
  AI: Ai;
  MY_QUEUE: Queue;
  
  // From .dev.vars (secrets)
  DATABASE_URL: string;
  API_KEY: string;
  
  // From wrangler.jsonc vars
  ENVIRONMENT: "development" | "staging" | "production";
  API_VERSION: string;
}
```

## Secrets Management

**CRITICAL: Never put secrets in wrangler.jsonc!** Secrets must be encrypted and hidden.

### Secrets vs Environment Variables

| Type | Storage | Use For | Visibility |
|------|---------|---------|------------|
| **vars** (wrangler.jsonc) | Plaintext | Non-sensitive config (URLs, flags) | ✅ Visible |
| **secrets** | Encrypted | API keys, passwords, tokens | ❌ Hidden |

### Local Development with .dev.vars

Create a `.dev.vars` file for local secrets (NEVER commit this file):

```bash
# .dev.vars (add to .gitignore)
DATABASE_URL="postgresql://localhost:5432/dev"
API_KEY="dev-key-12345"
STRIPE_SECRET="sk_test_..."
```

### CI/CD Best Practice: Empty .dev.vars

For CI/CD and type generation, commit a `.dev.vars` with **empty values**:

```bash
# .dev.vars (committed to git)
# Real values set via: wrangler secret put
DATABASE_URL=""
API_KEY=""
STRIPE_SECRET=""
```

**Why this works:**
- `wrangler types` reads `.dev.vars` to generate `Env` types
- Empty values create correct TypeScript types
- CI/CD can run type checking without real secrets
- Production secrets are set via `wrangler secret put` or dashboard

### Setting Production Secrets

**Via Wrangler:**
```bash
# Add/update secret (deploys immediately)
npx wrangler secret put API_KEY
# You'll be prompted for value

# List secrets (values never shown)
npx wrangler secret list

# Delete secret
npx wrangler secret delete API_KEY
```

**Via Dashboard:**
Workers & Pages → Your Worker → Settings → Variables and Secrets → Add → Secret

**Accessing secrets in code:**
```typescript
interface Env {
  DATABASE_URL: string;
  API_KEY: string;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Access secrets from env (same as regular env vars)
    const db = new Database(env.DATABASE_URL);
    
    // Validate API key
    const key = request.headers.get("x-api-key");
    if (key !== env.API_KEY) {
      return new Response("Unauthorized", { status: 401 });
    }
    
    return Response.json({ success: true });
  }
};
```

### Secret Store (Account-Level Secrets)

For secrets shared across multiple Workers:

```jsonc
{
  "secrets_store_secrets": [
    {
      "binding": "SHARED_API_KEY",
      "store_id": "abc123def456",
      "secret_name": "GLOBAL_API_KEY"
    }
  ]
}
```

**Accessing Secret Store:**
```typescript
interface Env {
  SHARED_API_KEY: {
    get(): Promise<string>;
  };
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    // Secret Store requires .get() call
    const apiKey = await env.SHARED_API_KEY.get();
    return Response.json({ success: true });
  }
};
```

See [references/secrets.md](references/secrets.md) for complete secrets management guide.

## wrangler.jsonc Configuration

Complete example with common bindings:

```jsonc
{
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_date": "2025-03-07",
  "compatibility_flags": ["nodejs_compat"],
  
  "observability": {
    "enabled": true,
    "head_sampling_rate": 1
  },
  
  "vars": {
    "ENVIRONMENT": "production"
  },
  
  "kv_namespaces": [
    { "binding": "MY_KV", "id": "your-kv-id" }
  ],
  
  "r
Files: 8
Size: 113.4 KB
Complexity: 51/100
Category: General

Related in General