Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    pascallammers

    cloudflare-workers

    pascallammers/cloudflare-workers
    Coding
    1
    1 installs

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    Auto-activates when user mentions Cloudflare Workers, edge functions, or serverless deployment. Expert in Cloudflare Workers including deployment, KV storage, and Durable Objects.

    SKILL.md

    Cloudflare Workers Skill

    Expert knowledge in Cloudflare Workers, edge computing, KV storage, Durable Objects, R2, and serverless deployment patterns.


    1. Worker Basics

    1.1 Worker Syntax & Structure

    Workers use the fetch event handler pattern to intercept and handle HTTP requests at the edge.

    ✅ Good: Standard fetch event handler

    export default {
      async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
        return new Response('Hello from the edge!', {
          status: 200,
          headers: { 'Content-Type': 'text/plain' },
        });
      },
    };
    

    ✅ Good: Module worker with typed environment

    interface Env {
      MY_KV: KVNamespace;
      MY_SECRET: string;
      MY_DURABLE_OBJECT: DurableObjectNamespace;
    }
    
    export default {
      async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
        const url = new URL(request.url);
        
        if (url.pathname === '/api/data') {
          const data = await env.MY_KV.get('key');
          return new Response(data, { status: 200 });
        }
        
        return new Response('Not found', { status: 404 });
      },
    };
    

    ❌ Bad: Using global state (resets between requests)

    // Global state is NOT persistent across requests!
    let requestCount = 0; // ❌ This resets unpredictably
    
    export default {
      async fetch(request: Request): Promise<Response> {
        requestCount++; // ❌ Unreliable counter
        return new Response(`Count: ${requestCount}`);
      },
    };
    

    ❌ Bad: Blocking synchronous operations

    export default {
      async fetch(request: Request): Promise<Response> {
        // ❌ Don't use synchronous blocking operations
        const data = someHeavyComputationSync(); // Wastes CPU time
        return new Response(data);
      },
    };
    

    1.2 Request & Response Objects

    Workers use standard Web APIs (Request/Response) for HTTP handling.

    ✅ Good: Parsing request data

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const url = new URL(request.url);
        const method = request.method;
        
        if (method === 'POST') {
          const contentType = request.headers.get('Content-Type');
          
          if (contentType?.includes('application/json')) {
            const body = await request.json();
            return Response.json({ received: body }, { status: 200 });
          }
          
          if (contentType?.includes('application/x-www-form-urlencoded')) {
            const formData = await request.formData();
            return Response.json({ fields: Object.fromEntries(formData) });
          }
        }
        
        return new Response('Method not allowed', { status: 405 });
      },
    };
    

    ✅ Good: Setting response headers and status

    export default {
      async fetch(request: Request): Promise<Response> {
        const data = { message: 'Success', timestamp: Date.now() };
        
        return new Response(JSON.stringify(data), {
          status: 200,
          headers: {
            'Content-Type': 'application/json',
            'Cache-Control': 'public, max-age=300',
            'X-Custom-Header': 'edge-response',
          },
        });
      },
    };
    

    1.3 Routing Patterns

    ✅ Good: Path-based routing

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const url = new URL(request.url);
        const path = url.pathname;
        
        if (path === '/') {
          return new Response('Home page');
        }
        
        if (path.startsWith('/api/')) {
          return handleAPI(request, env);
        }
        
        if (path.startsWith('/static/')) {
          return handleStatic(request, env);
        }
        
        return new Response('Not found', { status: 404 });
      },
    };
    
    async function handleAPI(request: Request, env: Env): Promise<Response> {
      return Response.json({ api: 'v1', status: 'ok' });
    }
    
    async function handleStatic(request: Request, env: Env): Promise<Response> {
      // Serve static assets from R2 or KV
      return new Response('Static content', { status: 200 });
    }
    

    ✅ Good: Using URL patterns with regex

    const routes = [
      { pattern: /^\/api\/users\/(\d+)$/, handler: getUserById },
      { pattern: /^\/api\/posts\/([a-z0-9-]+)$/, handler: getPostBySlug },
      { pattern: /^\/health$/, handler: healthCheck },
    ];
    
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const url = new URL(request.url);
        
        for (const route of routes) {
          const match = url.pathname.match(route.pattern);
          if (match) {
            return route.handler(request, env, match);
          }
        }
        
        return new Response('Not found', { status: 404 });
      },
    };
    
    async function getUserById(
      request: Request,
      env: Env,
      match: RegExpMatchArray
    ): Promise<Response> {
      const userId = match[1];
      return Response.json({ userId, name: 'John Doe' });
    }
    
    async function getPostBySlug(
      request: Request,
      env: Env,
      match: RegExpMatchArray
    ): Promise<Response> {
      const slug = match[1];
      const post = await env.POSTS_KV.get(slug);
      return new Response(post || 'Not found', { status: post ? 200 : 404 });
    }
    
    async function healthCheck(): Promise<Response> {
      return Response.json({ status: 'healthy', timestamp: Date.now() });
    }
    

    1.4 Local Development

    Use Wrangler for local development with hot reloading.

    ✅ Good: Local development workflow

    # Install Wrangler globally
    npm install -g wrangler
    
    # Create new Worker project
    wrangler init my-worker
    
    # Start local development server (port 8787 by default)
    wrangler dev
    
    # Start with remote resources (KV, Durable Objects)
    wrangler dev --remote
    
    # Start on custom port
    wrangler dev --port 3000
    
    # Start with live reload
    wrangler dev --live-reload
    

    ✅ Good: Testing locally with curl

    # Test GET request
    curl http://localhost:8787/
    
    # Test POST with JSON
    curl -X POST http://localhost:8787/api/data \
      -H "Content-Type: application/json" \
      -d '{"key":"value"}'
    
    # Test with headers
    curl http://localhost:8787/api/users/123 \
      -H "Authorization: Bearer token"
    

    1.5 Deployment

    ✅ Good: Deploy to production

    # Deploy Worker
    wrangler deploy
    
    # Deploy to specific environment
    wrangler deploy --env production
    
    # Deploy with verbose logging
    wrangler deploy --verbose
    
    # Tail logs after deployment
    wrangler tail
    

    ✅ Good: wrangler.toml configuration

    name = "my-worker"
    main = "src/index.ts"
    compatibility_date = "2024-11-01"
    
    # Routes (alternative to dashboard configuration)
    routes = [
      { pattern = "example.com/*", zone_name = "example.com" }
    ]
    
    # Environment variables
    [vars]
    ENVIRONMENT = "production"
    API_VERSION = "v1"
    
    # KV bindings
    [[kv_namespaces]]
    binding = "MY_KV"
    id = "abc123"
    
    # Durable Object bindings
    [[durable_objects.bindings]]
    name = "MY_DO"
    class_name = "MyDurableObject"
    script_name = "my-worker"
    
    # R2 bindings
    [[r2_buckets]]
    binding = "MY_BUCKET"
    bucket_name = "my-bucket"
    

    2. KV Storage (Key-Value)

    Workers KV is a global, low-latency key-value store optimized for high read volumes and infrequent writes.

    2.1 KV Operations

    ✅ Good: Basic KV operations

    interface Env {
      MY_KV: KVNamespace;
    }
    
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const url = new URL(request.url);
        const key = url.searchParams.get('key');
        
        if (!key) {
          return new Response('Key required', { status: 400 });
        }
        
        // GET: Read value
        const value = await env.MY_KV.get(key);
        
        if (value === null) {
          return new Response('Not found', { status: 404 });
        }
        
        return new Response(value, { status: 200 });
      },
    };
    

    ✅ Good: PUT with metadata and TTL

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const { key, value, ttl } = await request.json();
        
        // Put with metadata and expiration
        await env.MY_KV.put(key, value, {
          expirationTtl: ttl || 3600, // 1 hour default
          metadata: {
            createdAt: Date.now(),
            version: '1.0',
            author: 'worker',
          },
        });
        
        return Response.json({ success: true, key });
      },
    };
    

    ✅ Good: GET with metadata

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const key = 'user:123';
        
        // Get value with metadata
        const { value, metadata } = await env.MY_KV.getWithMetadata(key);
        
        if (value === null) {
          return new Response('Not found', { status: 404 });
        }
        
        return Response.json({
          value,
          metadata, // Custom metadata attached to the key
        });
      },
    };
    

    ✅ Good: DELETE operation

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const key = 'session:abc123';
        
        // Delete key
        await env.MY_KV.delete(key);
        
        return Response.json({ deleted: true, key });
      },
    };
    

    2.2 KV Namespaces

    KV data is organized into namespaces (isolated storage buckets).

    ✅ Good: Multiple namespaces for different data types

    # wrangler.toml
    [[kv_namespaces]]
    binding = "USERS"
    id = "abc123"
    
    [[kv_namespaces]]
    binding = "SESSIONS"
    id = "def456"
    
    [[kv_namespaces]]
    binding = "CACHE"
    id = "ghi789"
    
    interface Env {
      USERS: KVNamespace;
      SESSIONS: KVNamespace;
      CACHE: KVNamespace;
    }
    
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        // Different namespaces for different purposes
        const user = await env.USERS.get('user:123');
        const session = await env.SESSIONS.get('session:abc');
        const cachedData = await env.CACHE.get('api:response:xyz');
        
        return Response.json({ user, session, cachedData });
      },
    };
    

    2.3 TTL and Expiration

    ✅ Good: Using TTL for automatic expiration

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const sessionId = 'session:abc123';
        const sessionData = JSON.stringify({ userId: 123, loggedIn: true });
        
        // Expire after 1 hour (3600 seconds)
        await env.SESSIONS.put(sessionId, sessionData, {
          expirationTtl: 3600,
        });
        
        return Response.json({ message: 'Session created with 1-hour TTL' });
      },
    };
    

    ✅ Good: Using absolute expiration timestamp

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const cacheKey = 'cache:data';
        const data = JSON.stringify({ result: 'cached' });
        
        // Expire at specific Unix timestamp (24 hours from now)
        const expirationTime = Math.floor(Date.now() / 1000) + 86400;
        
        await env.CACHE.put(cacheKey, data, {
          expiration: expirationTime,
        });
        
        return Response.json({ message: 'Cached until tomorrow' });
      },
    };
    

    ❌ Bad: Not using TTL for temporary data

    // ❌ Storing temporary data without expiration
    await env.SESSIONS.put('session:temp', 'data'); // Never expires, wastes storage
    

    2.4 Bulk Operations

    ✅ Good: Listing keys with pagination

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const allKeys: string[] = [];
        let cursor: string | undefined;
        
        // Paginate through all keys
        do {
          const result = await env.MY_KV.list({ cursor, limit: 1000 });
          allKeys.push(...result.keys.map((k) => k.name));
          cursor = result.cursor;
        } while (cursor);
        
        return Response.json({ keys: allKeys, total: allKeys.length });
      },
    };
    

    ✅ Good: Listing keys with prefix filter

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        // List all user keys (e.g., "user:123", "user:456")
        const result = await env.USERS.list({ prefix: 'user:' });
        
        const userKeys = result.keys.map((k) => k.name);
        
        return Response.json({ users: userKeys });
      },
    };
    

    ❌ Bad: Fetching all keys at once without pagination

    // ❌ Doesn't handle pagination, limited to 1000 keys
    const result = await env.MY_KV.list();
    const keys = result.keys.map((k) => k.name); // Only gets first 1000 keys
    

    2.5 Caching Patterns

    ✅ Good: Cache-aside pattern

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const userId = 'user:123';
        
        // Try to get from cache first
        let user = await env.CACHE.get(userId);
        
        if (!user) {
          // Cache miss - fetch from origin/database
          user = await fetchUserFromDatabase(userId);
          
          // Store in cache for 5 minutes
          await env.CACHE.put(userId, user, { expirationTtl: 300 });
        }
        
        return new Response(user, { status: 200 });
      },
    };
    
    async function fetchUserFromDatabase(userId: string): Promise<string> {
      // Simulate database fetch
      return JSON.stringify({ id: userId, name: 'John Doe' });
    }
    

    ✅ Good: Cache invalidation on updates

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const url = new URL(request.url);
        const userId = url.searchParams.get('userId');
        
        if (request.method === 'PUT') {
          const newData = await request.json();
          
          // Update database
          await updateDatabase(userId!, newData);
          
          // Invalidate cache
          await env.CACHE.delete(`user:${userId}`);
          
          return Response.json({ message: 'User updated, cache invalidated' });
        }
        
        return new Response('Method not allowed', { status: 405 });
      },
    };
    
    async function updateDatabase(userId: string, data: any): Promise<void> {
      // Update database logic
    }
    

    ❌ Bad: Storing large values in KV

    // ❌ KV has a 25 MB limit per key
    const largeFile = await fetch('https://example.com/large-file.zip').then(r => r.arrayBuffer());
    await env.MY_KV.put('file', largeFile); // ❌ May fail if > 25 MB
    

    ❌ Bad: Using KV for high-write workloads

    // ❌ KV is optimized for reads, not writes
    for (let i = 0; i < 10000; i++) {
      await env.MY_KV.put(`counter:${i}`, String(i)); // ❌ Slow, use Durable Objects instead
    }
    

    2.6 KV Limits

    Limit Free Paid
    Reads/day 100,000 Unlimited
    Writes/day 1,000 Unlimited
    Deletes/day 1,000 Unlimited
    Lists/day 1,000 Unlimited
    Key size 512 bytes 512 bytes
    Value size 25 MB 25 MB
    Metadata size 1024 bytes 1024 bytes

    3. Durable Objects

    Durable Objects provide strongly consistent, stateful storage at the edge with built-in coordination.

    3.1 Creating Durable Object Classes

    ✅ Good: Basic Durable Object structure

    export class Counter {
      private state: DurableObjectState;
      private count: number = 0;
      
      constructor(state: DurableObjectState, env: Env) {
        this.state = state;
        
        // Load persisted count from storage
        this.state.blockConcurrencyWhile(async () => {
          const stored = await this.state.storage.get<number>('count');
          this.count = stored || 0;
        });
      }
      
      async fetch(request: Request): Promise<Response> {
        const url = new URL(request.url);
        
        if (url.pathname === '/increment') {
          this.count++;
          await this.state.storage.put('count', this.count);
          return Response.json({ count: this.count });
        }
        
        if (url.pathname === '/get') {
          return Response.json({ count: this.count });
        }
        
        return new Response('Not found', { status: 404 });
      }
    }
    

    ✅ Good: Durable Object with alarm

    export class Scheduler {
      private state: DurableObjectState;
      
      constructor(state: DurableObjectState, env: Env) {
        this.state = state;
      }
      
      async fetch(request: Request): Promise<Response> {
        const { taskId, delay } = await request.json();
        
        // Schedule alarm for future execution
        const alarmTime = Date.now() + delay;
        await this.state.storage.setAlarm(alarmTime);
        await this.state.storage.put('taskId', taskId);
        
        return Response.json({ scheduled: true, alarmTime });
      }
      
      async alarm(): Promise<void> {
        // Called when alarm triggers
        const taskId = await this.state.storage.get<string>('taskId');
        console.log(`Executing task: ${taskId}`);
        
        // Perform scheduled work
        await this.executeTask(taskId);
      }
      
      private async executeTask(taskId: string | undefined): Promise<void> {
        // Task execution logic
      }
    }
    

    3.2 Instance Coordination

    ✅ Good: Using Durable Objects for coordination

    export class RateLimiter {
      private state: DurableObjectState;
      
      constructor(state: DurableObjectState, env: Env) {
        this.state = state;
      }
      
      async fetch(request: Request): Promise<Response> {
        const { userId, limit, window } = await request.json();
        
        const key = `requests:${userId}`;
        const now = Date.now();
        
        // Get request timestamps
        let timestamps = await this.state.storage.get<number[]>(key) || [];
        
        // Filter out old timestamps outside the window
        timestamps = timestamps.filter(ts => now - ts < window);
        
        if (timestamps.length >= limit) {
          return Response.json({ allowed: false, remaining: 0 }, { status: 429 });
        }
        
        // Add current timestamp
        timestamps.push(now);
        await this.state.storage.put(key, timestamps);
        
        return Response.json({
          allowed: true,
          remaining: limit - timestamps.length,
        });
      }
    }
    

    ✅ Good: Accessing Durable Objects from Workers

    interface Env {
      COUNTER: DurableObjectNamespace;
      RATE_LIMITER: DurableObjectNamespace;
    }
    
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        // Get Durable Object instance by unique ID
        const id = env.COUNTER.idFromName('global-counter');
        const stub = env.COUNTER.get(id);
        
        // Forward request to Durable Object
        return stub.fetch(request);
      },
    };
    

    ✅ Good: Using unique IDs for isolation

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const userId = 'user:123';
        
        // Each user gets their own Durable Object instance
        const id = env.RATE_LIMITER.idFromName(userId);
        const stub = env.RATE_LIMITER.get(id);
        
        const response = await stub.fetch('http://fake-host/check', {
          method: 'POST',
          body: JSON.stringify({ userId, limit: 100, window: 60000 }),
        });
        
        return response;
      },
    };
    

    3.3 WebSocket Connections

    ✅ Good: WebSocket server in Durable Object

    export class ChatRoom {
      private state: DurableObjectState;
      private sessions: Set<WebSocket> = new Set();
      
      constructor(state: DurableObjectState, env: Env) {
        this.state = state;
      }
      
      async fetch(request: Request): Promise<Response> {
        // Upgrade to WebSocket
        const upgradeHeader = request.headers.get('Upgrade');
        if (upgradeHeader !== 'websocket') {
          return new Response('Expected WebSocket', { status: 400 });
        }
        
        const pair = new WebSocketPair();
        const [client, server] = Object.values(pair);
        
        // Accept WebSocket connection
        this.state.acceptWebSocket(server);
        this.sessions.add(server);
        
        server.addEventListener('message', (event) => {
          // Broadcast message 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 });
      }
      
      private broadcast(message: string): void {
        for (const session of this.sessions) {
          try {
            session.send(message);
          } catch (err) {
            this.sessions.delete(session);
          }
        }
      }
      
      async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): Promise<void> {
        // Handle incoming WebSocket messages
        this.broadcast(typeof message === 'string' ? message : 'Binary message');
      }
      
      async webSocketClose(ws: WebSocket, code: number, reason: string): Promise<void> {
        this.sessions.delete(ws);
      }
    }
    

    ✅ Good: WebSocket hibernation for scalability

    export class ScalableChatRoom {
      private state: DurableObjectState;
      
      constructor(state: DurableObjectState, env: Env) {
        this.state = state;
        
        // Enable WebSocket hibernation to reduce memory usage
        this.state.setWebSocketAutoResponse(
          new WebSocketRequestResponsePair(
            JSON.stringify({ type: 'ping' }),
            JSON.stringify({ type: 'pong' })
          )
        );
      }
      
      async fetch(request: Request): Promise<Response> {
        const pair = new WebSocketPair();
        const [client, server] = Object.values(pair);
        
        // Accept with hibernation enabled
        this.state.acceptWebSocket(server);
        
        return new Response(null, { status: 101, webSocket: client });
      }
      
      async webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): Promise<void> {
        // Only called when message is received (not during hibernation)
        const msg = typeof message === 'string' ? message : new TextDecoder().decode(message);
        const data = JSON.parse(msg);
        
        // Broadcast to all active WebSockets
        this.state.getWebSockets().forEach((socket) => {
          socket.send(JSON.stringify({ from: 'server', data }));
        });
      }
    }
    

    3.4 Use Cases

    ✅ Good: Collaborative editing

    export class Document {
      private state: DurableObjectState;
      private content: string = '';
      
      constructor(state: DurableObjectState, env: Env) {
        this.state = state;
        
        this.state.blockConcurrencyWhile(async () => {
          this.content = await this.state.storage.get<string>('content') || '';
        });
      }
      
      async fetch(request: Request): Promise<Response> {
        const url = new URL(request.url);
        
        if (url.pathname === '/edit') {
          const { operation, position, text } = await request.json();
          
          // Apply edit operation
          if (operation === 'insert') {
            this.content = this.content.slice(0, position) + text + this.content.slice(position);
          } else if (operation === 'delete') {
            this.content = this.content.slice(0, position) + this.content.slice(position + text.length);
          }
          
          // Persist changes
          await this.state.storage.put('content', this.content);
          
          return Response.json({ success: true, content: this.content });
        }
        
        if (url.pathname === '/get') {
          return Response.json({ content: this.content });
        }
        
        return new Response('Not found', { status: 404 });
      }
    }
    

    ✅ Good: Global counters

    export class PageViewCounter {
      private state: DurableObjectState;
      private views: Map<string, number> = new Map();
      
      constructor(state: DurableObjectState, env: Env) {
        this.state = state;
      }
      
      async fetch(request: Request): Promise<Response> {
        const { page } = await request.json();
        
        const currentViews = this.views.get(page) || 0;
        const newViews = currentViews + 1;
        
        this.views.set(page, newViews);
        
        // Persist to storage
        await this.state.storage.put(`views:${page}`, newViews);
        
        return Response.json({ page, views: newViews });
      }
    }
    

    ❌ Bad: Using Durable Objects for stateless operations

    // ❌ Don't use Durable Objects for simple stateless operations
    export class SimpleAPI {
      async fetch(request: Request): Promise<Response> {
        // ❌ No state needed, use regular Worker instead
        return Response.json({ message: 'Hello' });
      }
    }
    

    ❌ Bad: Not persisting state to storage

    export class BadCounter {
      private count: number = 0; // ❌ Only in memory, lost on restart
      
      async fetch(request: Request): Promise<Response> {
        this.count++; // ❌ Not persisted to storage
        return Response.json({ count: this.count });
      }
    }
    

    4. R2 Storage

    R2 is Cloudflare's object storage for large files (images, videos, backups, etc.) with S3-compatible API.

    4.1 Upload & Download

    ✅ Good: Upload file to R2

    interface Env {
      MY_BUCKET: R2Bucket;
    }
    
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        if (request.method === 'POST') {
          const formData = await request.formData();
          const file = formData.get('file') as File;
          
          if (!file) {
            return new Response('No file provided', { status: 400 });
          }
          
          // Upload to R2
          await env.MY_BUCKET.put(file.name, file.stream(), {
            httpMetadata: {
              contentType: file.type,
            },
            customMetadata: {
              uploadedBy: 'worker',
              uploadedAt: new Date().toISOString(),
            },
          });
          
          return Response.json({ success: true, filename: file.name });
        }
        
        return new Response('Method not allowed', { status: 405 });
      },
    };
    

    ✅ Good: Download file from R2

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const url = new URL(request.url);
        const key = url.pathname.slice(1); // Remove leading slash
        
        // Get object from R2
        const object = await env.MY_BUCKET.get(key);
        
        if (!object) {
          return new Response('Object not found', { status: 404 });
        }
        
        // Return object with proper headers
        return new Response(object.body, {
          headers: {
            'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
            'Content-Length': String(object.size),
            'ETag': object.etag,
            'Cache-Control': 'public, max-age=3600',
          },
        });
      },
    };
    

    ✅ Good: Upload with custom metadata

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const { key, content, metadata } = await request.json();
        
        await env.MY_BUCKET.put(key, content, {
          httpMetadata: {
            contentType: 'application/json',
            contentEncoding: 'gzip',
            cacheControl: 'public, max-age=31536000',
          },
          customMetadata: {
            version: metadata.version,
            author: metadata.author,
            tags: JSON.stringify(metadata.tags),
          },
        });
        
        return Response.json({ success: true });
      },
    };
    

    4.2 List & Delete

    ✅ Good: List objects with pagination

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const url = new URL(request.url);
        const prefix = url.searchParams.get('prefix') || '';
        const limit = parseInt(url.searchParams.get('limit') || '1000');
        
        const listed = await env.MY_BUCKET.list({
          prefix,
          limit,
        });
        
        const objects = listed.objects.map(obj => ({
          key: obj.key,
          size: obj.size,
          uploaded: obj.uploaded,
          etag: obj.etag,
        }));
        
        return Response.json({
          objects,
          truncated: listed.truncated,
          cursor: listed.cursor,
        });
      },
    };
    

    ✅ Good: Delete object

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        if (request.method === 'DELETE') {
          const url = new URL(request.url);
          const key = url.pathname.slice(1);
          
          await env.MY_BUCKET.delete(key);
          
          return Response.json({ deleted: true, key });
        }
        
        return new Response('Method not allowed', { status: 405 });
      },
    };
    

    4.3 Multipart Uploads

    ✅ Good: Multipart upload for large files

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const key = 'large-file.zip';
        
        // Start multipart upload
        const multipart = await env.MY_BUCKET.createMultipartUpload(key);
        
        const parts: R2UploadedPart[] = [];
        const chunkSize = 5 * 1024 * 1024; // 5 MB chunks
        
        // Upload parts (simulated)
        for (let i = 0; i < 10; i++) {
          const chunk = new Uint8Array(chunkSize); // Simulate chunk data
          
          const uploadedPart = await multipart.uploadPart(i + 1, chunk);
          parts.push(uploadedPart);
        }
        
        // Complete multipart upload
        const object = await multipart.complete(parts);
        
        return Response.json({ success: true, etag: object.etag });
      },
    };
    

    ❌ Bad: Uploading large files without multipart

    // ❌ Don't upload files > 100 MB in single request
    const largeFile = new Uint8Array(200 * 1024 * 1024); // 200 MB
    await env.MY_BUCKET.put('large.bin', largeFile); // ❌ May timeout or fail
    

    4.4 Public Buckets & Presigned URLs

    ✅ Good: Serve public files from R2

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const url = new URL(request.url);
        const key = url.pathname.slice(1);
        
        const object = await env.MY_BUCKET.get(key);
        
        if (!object) {
          return new Response('Not found', { status: 404 });
        }
        
        // Serve with caching headers
        return new Response(object.body, {
          headers: {
            'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
            'Cache-Control': 'public, max-age=86400',
            'ETag': object.etag,
          },
        });
      },
    };
    

    ✅ Good: Generate presigned URL (alternative pattern)

    // R2 doesn't have native presigned URLs, but you can create signed tokens
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const key = 'private/document.pdf';
        const token = await generateToken(key, env.SECRET_KEY);
        
        const signedUrl = `https://my-worker.example.com/download/${key}?token=${token}`;
        
        return Response.json({ url: signedUrl });
      },
    };
    
    async function generateToken(key: string, secret: string): Promise<string> {
      const data = `${key}:${Date.now() + 3600000}`; // Expires in 1 hour
      const encoder = new TextEncoder();
      const keyData = encoder.encode(secret);
      const algorithm = { name: 'HMAC', hash: 'SHA-256' };
      const cryptoKey = await crypto.subtle.importKey('raw', keyData, algorithm, false, ['sign']);
      const signature = await crypto.subtle.sign(algorithm.name, cryptoKey, encoder.encode(data));
      return btoa(String.fromCharCode(...new Uint8Array(signature)));
    }
    

    ❌ Bad: Exposing sensitive files without authentication

    // ❌ Serving private files without access control
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const key = 'private/secret.pdf';
        const object = await env.MY_BUCKET.get(key);
        return new Response(object?.body); // ❌ No authentication!
      },
    };
    

    5. Bindings

    Bindings connect Workers to Cloudflare resources (KV, Durable Objects, R2, etc.).

    5.1 KV Bindings

    ✅ Good: KV binding in wrangler.toml

    [[kv_namespaces]]
    binding = "MY_KV"
    id = "abc123def456"
    
    [[kv_namespaces]]
    binding = "CACHE"
    id = "xyz789"
    preview_id = "preview123" # For local dev
    

    ✅ Good: Using KV bindings in TypeScript

    interface Env {
      MY_KV: KVNamespace;
      CACHE: KVNamespace;
    }
    
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const data = await env.MY_KV.get('key');
        return new Response(data);
      },
    };
    

    5.2 Durable Object Bindings

    ✅ Good: Durable Object binding

    [[durable_objects.bindings]]
    name = "COUNTER"
    class_name = "Counter"
    script_name = "my-worker" # Optional if in same script
    
    [[migrations]]
    tag = "v1"
    new_classes = ["Counter"]
    
    interface Env {
      COUNTER: DurableObjectNamespace;
    }
    
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const id = env.COUNTER.idFromName('global');
        const stub = env.COUNTER.get(id);
        return stub.fetch(request);
      },
    };
    
    export class Counter {
      private state: DurableObjectState;
      
      constructor(state: DurableObjectState, env: Env) {
        this.state = state;
      }
      
      async fetch(request: Request): Promise<Response> {
        return Response.json({ count: 123 });
      }
    }
    

    5.3 R2 Bindings

    ✅ Good: R2 bucket binding

    [[r2_buckets]]
    binding = "MY_BUCKET"
    bucket_name = "my-bucket-name"
    
    [[r2_buckets]]
    binding = "ASSETS"
    bucket_name = "static-assets"
    
    interface Env {
      MY_BUCKET: R2Bucket;
      ASSETS: R2Bucket;
    }
    
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const file = await env.ASSETS.get('logo.png');
        return new Response(file?.body);
      },
    };
    

    5.4 Service Bindings

    Service bindings allow Workers to call other Workers directly (RPC-style).

    ✅ Good: Service binding configuration

    # worker-a/wrangler.toml
    name = "worker-a"
    
    [[services]]
    binding = "WORKER_B"
    service = "worker-b"
    
    // worker-a/index.ts
    interface Env {
      WORKER_B: Fetcher;
    }
    
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        // Call worker-b directly (no HTTP overhead)
        const response = await env.WORKER_B.fetch('http://fake-host/api/data');
        const data = await response.json();
        
        return Response.json({ fromWorkerB: data });
      },
    };
    
    // worker-b/index.ts
    export default {
      async fetch(request: Request): Promise<Response> {
        return Response.json({ message: 'Hello from Worker B' });
      },
    };
    

    5.5 Environment Variables & Secrets

    ✅ Good: Environment variables

    [vars]
    ENVIRONMENT = "production"
    API_VERSION = "v1"
    MAX_RETRIES = "3"
    
    interface Env {
      ENVIRONMENT: string;
      API_VERSION: string;
      MAX_RETRIES: string;
    }
    
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        return Response.json({
          env: env.ENVIRONMENT,
          version: env.API_VERSION,
        });
      },
    };
    

    ✅ Good: Managing secrets with Wrangler

    # Set secret (not stored in wrangler.toml)
    wrangler secret put API_KEY
    # Enter secret value when prompted
    
    # Delete secret
    wrangler secret delete API_KEY
    
    # List secrets
    wrangler secret list
    
    interface Env {
      API_KEY: string; // Secret from Wrangler
    }
    
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const response = await fetch('https://api.example.com/data', {
          headers: { 'Authorization': `Bearer ${env.API_KEY}` },
        });
        
        return response;
      },
    };
    

    ❌ Bad: Hardcoding secrets in code

    // ❌ Never hardcode secrets!
    const API_KEY = 'sk-1234567890abcdef'; // ❌ Exposed in source code
    
    export default {
      async fetch(request: Request): Promise<Response> {
        const response = await fetch('https://api.example.com', {
          headers: { 'Authorization': `Bearer ${API_KEY}` },
        });
        return response;
      },
    };
    

    6. Performance & Limits

    6.1 CPU Time Limits

    Plan CPU Time (HTTP) CPU Time (Cron)
    Free 10 ms 10 ms
    Paid 30 seconds 15 minutes

    ✅ Good: Efficient CPU usage

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        // Offload heavy computation to external API or Durable Object
        const result = await fetch('https://compute-service.example.com/heavy-task');
        return result;
      },
    };
    

    ❌ Bad: CPU-intensive operations

    // ❌ Heavy computation exceeds CPU limits
    export default {
      async fetch(request: Request): Promise<Response> {
        let result = 0;
        for (let i = 0; i < 1_000_000_000; i++) {
          result += Math.sqrt(i); // ❌ Exceeds 10ms CPU limit
        }
        return Response.json({ result });
      },
    };
    

    6.2 Memory Limits

    Workers have a 128 MB memory limit.

    ✅ Good: Streaming large responses

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const object = await env.MY_BUCKET.get('large-file.mp4');
        
        // Stream directly (doesn't load into memory)
        return new Response(object?.body, {
          headers: { 'Content-Type': 'video/mp4' },
        });
      },
    };
    

    ❌ Bad: Loading large files into memory

    // ❌ Loading entire file into memory
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const object = await env.MY_BUCKET.get('large-file.mp4');
        const arrayBuffer = await object?.arrayBuffer(); // ❌ May exceed 128 MB
        return new Response(arrayBuffer);
      },
    };
    

    6.3 Request & Response Limits

    Limit Value
    Request URL 16 KB
    Request headers 128 KB
    Request body (Free/Pro) 100 MB
    Request body (Business) 200 MB
    Request body (Enterprise) 500 MB
    Response headers 128 KB
    Subrequests 50 (Free), 1000 (Paid)

    ✅ Good: Handling subrequest limits

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        // Batch requests to stay under subrequest limit
        const urls = ['url1', 'url2', 'url3']; // Only 3 subrequests
        
        const responses = await Promise.all(
          urls.map(url => fetch(`https://api.example.com/${url}`))
        );
        
        const data = await Promise.all(responses.map(r => r.json()));
        
        return Response.json(data);
      },
    };
    

    ❌ Bad: Exceeding subrequest limits

    // ❌ Too many subrequests on free plan
    export default {
      async fetch(request: Request): Promise<Response> {
        const promises = [];
        for (let i = 0; i < 100; i++) {
          promises.push(fetch(`https://api.example.com/item/${i}`)); // ❌ Exceeds 50 subrequests
        }
        await Promise.all(promises);
        return new Response('Done');
      },
    };
    

    6.4 KV Limits (Repeated for Reference)

    Limit Free Paid
    Reads/day 100,000 Unlimited
    Writes/day 1,000 Unlimited
    Key size 512 bytes 512 bytes
    Value size 25 MB 25 MB

    6.5 Optimization Strategies

    ✅ Good: Caching responses

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const cache = caches.default;
        const cacheKey = new Request(request.url, request);
        
        // Try cache first
        let response = await cache.match(cacheKey);
        
        if (!response) {
          // Cache miss - fetch from origin
          response = await fetch(request);
          
          // Cache for 1 hour
          response = new Response(response.body, response);
          response.headers.set('Cache-Control', 'public, max-age=3600');
          
          await cache.put(cacheKey, response.clone());
        }
        
        return response;
      },
    };
    

    ✅ Good: Lazy loading and code splitting

    // Use dynamic imports to reduce initial bundle size
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const url = new URL(request.url);
        
        if (url.pathname.startsWith('/admin')) {
          // Only load admin module when needed
          const { handleAdmin } = await import('./admin');
          return handleAdmin(request, env);
        }
        
        return new Response('Home page');
      },
    };
    

    7. Wrangler CLI

    Wrangler is the official CLI tool for managing Cloudflare Workers.

    7.1 Project Initialization

    ✅ Good: Create new Worker project

    # Create new project with interactive prompts
    wrangler init my-worker
    
    # Create with TypeScript
    wrangler init my-worker --type typescript
    
    # Create from template
    wrangler init my-worker --from-dash
    

    ✅ Good: Project structure

    my-worker/
    ├── src/
    │   └── index.ts
    ├── wrangler.toml
    ├── package.json
    └── tsconfig.json
    

    7.2 Configuration (wrangler.toml)

    ✅ Good: Complete wrangler.toml example

    name = "my-worker"
    main = "src/index.ts"
    compatibility_date = "2024-11-01"
    compatibility_flags = ["nodejs_compat"]
    
    # Workers Paid plan
    workers_dev = true
    account_id = "your-account-id"
    
    # Routes
    routes = [
      { pattern = "example.com/api/*", zone_name = "example.com" }
    ]
    
    # Environment variables
    [vars]
    ENVIRONMENT = "production"
    LOG_LEVEL = "info"
    
    # KV namespaces
    [[kv_namespaces]]
    binding = "MY_KV"
    id = "abc123"
    
    # Durable Objects
    [[durable_objects.bindings]]
    name = "COUNTER"
    class_name = "Counter"
    script_name = "my-worker"
    
    # R2 buckets
    [[r2_buckets]]
    binding = "MY_BUCKET"
    bucket_name = "my-bucket"
    
    # Service bindings
    [[services]]
    binding = "AUTH_SERVICE"
    service = "auth-worker"
    
    # Build configuration
    [build]
    command = "npm run build"
    
    [build.upload]
    format = "modules"
    main = "./dist/index.js"
    
    # Environments
    [env.staging]
    vars = { ENVIRONMENT = "staging" }
    routes = [{ pattern = "staging.example.com/*", zone_name = "example.com" }]
    
    [env.production]
    vars = { ENVIRONMENT = "production" }
    routes = [{ pattern = "example.com/*", zone_name = "example.com" }]
    

    7.3 Local Development

    ✅ Good: Local dev workflows

    # Start local dev server
    wrangler dev
    
    # Dev with remote resources (KV, Durable Objects)
    wrangler dev --remote
    
    # Dev with specific port
    wrangler dev --port 3000
    
    # Dev with live reload
    wrangler dev --live-reload
    
    # Dev with specific environment
    wrangler dev --env staging
    
    # Test locally
    curl http://localhost:8787/api/test
    

    7.4 Deployment

    ✅ Good: Deployment commands

    # Deploy to production
    wrangler deploy
    
    # Deploy to specific environment
    wrangler deploy --env staging
    
    # Dry run (validate without deploying)
    wrangler deploy --dry-run
    
    # Deploy with verbose output
    wrangler deploy --verbose
    
    # Rollback to previous version
    wrangler rollback
    

    7.5 Secrets Management

    ✅ Good: Managing secrets securely

    # Add secret
    wrangler secret put API_KEY
    # Prompt: Enter a secret value: ********
    
    # Add secret for specific environment
    wrangler secret put API_KEY --env production
    
    # List secrets
    wrangler secret list
    
    # Delete secret
    wrangler secret delete API_KEY
    
    # Bulk secret management
    echo "SECRET_VALUE" | wrangler secret put SECRET_KEY
    

    7.6 Tailing Logs

    ✅ Good: Real-time log monitoring

    # Tail logs in real-time
    wrangler tail
    
    # Tail with filtering
    wrangler tail --status error
    
    # Tail specific environment
    wrangler tail --env production
    
    # Tail with JSON output
    wrangler tail --format json
    
    # Sample logs
    wrangler tail --sampling-rate 0.1  # 10% of requests
    
    // Logging in Worker
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        console.log('Request received:', request.url);
        console.error('Error occurred:', new Error('Sample error'));
        console.warn('Warning: High memory usage');
        
        return new Response('OK');
      },
    };
    

    ✅ Good: KV management via CLI

    # Create KV namespace
    wrangler kv:namespace create "MY_KV"
    
    # List namespaces
    wrangler kv:namespace list
    
    # Put key-value
    wrangler kv:key put --binding=MY_KV "key" "value"
    
    # Get value
    wrangler kv:key get --binding=MY_KV "key"
    
    # Delete key
    wrangler kv:key delete --binding=MY_KV "key"
    
    # List keys
    wrangler kv:key list --binding=MY_KV --prefix="user:"
    
    # Bulk upload from JSON
    wrangler kv:bulk put --binding=MY_KV data.json
    

    8. Use Cases & Patterns

    8.1 API Proxying

    ✅ Good: Proxy with caching

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const url = new URL(request.url);
        
        // Proxy to backend API
        const backendUrl = `https://api.backend.com${url.pathname}`;
        
        // Check cache first
        const cacheKey = new Request(backendUrl);
        const cachedResponse = await caches.default.match(cacheKey);
        
        if (cachedResponse) {
          return cachedResponse;
        }
        
        // Fetch from backend
        const response = await fetch(backendUrl, {
          headers: {
            'Authorization': `Bearer ${env.API_KEY}`,
          },
        });
        
        // Cache successful responses
        if (response.ok) {
          const responseToCache = response.clone();
          await caches.default.put(cacheKey, responseToCache);
        }
        
        return response;
      },
    };
    

    8.2 A/B Testing

    ✅ Good: Edge-based A/B testing

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const url = new URL(request.url);
        
        // Determine variant based on cookie or random assignment
        const variant = getVariant(request);
        
        if (variant === 'A') {
          return fetch(`https://variant-a.example.com${url.pathname}`);
        } else {
          return fetch(`https://variant-b.example.com${url.pathname}`);
        }
      },
    };
    
    function getVariant(request: Request): 'A' | 'B' {
      const cookie = request.headers.get('Cookie');
      
      if (cookie?.includes('variant=A')) {
        return 'A';
      }
      
      if (cookie?.includes('variant=B')) {
        return 'B';
      }
      
      // Random assignment (50/50)
      return Math.random() < 0.5 ? 'A' : 'B';
    }
    

    8.3 Geo-Routing

    ✅ Good: Route based on location

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const country = request.cf?.country as string;
        
        // Route to regional backend
        if (country === 'US' || country === 'CA') {
          return fetch('https://us-backend.example.com', request);
        } else if (country === 'GB' || country === 'DE' || country === 'FR') {
          return fetch('https://eu-backend.example.com', request);
        } else {
          return fetch('https://global-backend.example.com', request);
        }
      },
    };
    

    8.4 Authentication at the Edge

    ✅ Good: JWT validation at the edge

    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        const authHeader = request.headers.get('Authorization');
        
        if (!authHeader?.startsWith('Bearer ')) {
          return new Response('Unauthorized', { status: 401 });
        }
        
        const token = authHeader.slice(7);
        
        // Validate JWT (simplified)
        const isValid = await validateJWT(token, env.JWT_SECRET);
        
        if (!isValid) {
          return new Response('Invalid token', { status: 403 });
        }
        
        // Forward to backend
        return fetch('https://backend.example.com', request);
      },
    };
    
    async function validateJWT(token: string, secret: string): Promise<boolean> {
      // JWT validation logic (use library like jose)
      return true; // Simplified
    }
    

    ❌ Bad: Insecure authentication

    // ❌ Never expose secrets in responses
    export default {
      async fetch(request: Request, env: Env): Promise<Response> {
        return Response.json({ secret: env.API_KEY }); // ❌ Exposed!
      },
    };
    

    Summary

    This comprehensive Cloudflare Workers skill covers:

    1. Worker Basics: Fetch handlers, routing, local dev, deployment
    2. KV Storage: Key-value operations, TTL, caching patterns, limits
    3. Durable Objects: Stateful computing, WebSockets, coordination, alarms
    4. R2 Storage: Object storage, uploads, downloads, multipart uploads
    5. Bindings: KV, Durable Objects, R2, service bindings, secrets
    6. Performance & Limits: CPU, memory, request limits, optimization
    7. Wrangler CLI: Init, config, dev, deploy, secrets, logs
    8. Use Cases: API proxying, A/B testing, geo-routing, edge auth

    Use Cloudflare Workers for edge computing, low-latency APIs, global state management, and serverless applications at scale.

    Recommended Servers
    Kernel
    Kernel
    Vercel
    Vercel
    Repository
    pascallammers/mylo-travel-concierge-v2
    Files