# Cloudflare Workers Reference Sheet

# Core Concepts

**Workers Runtime**: V8 isolates (not Node.js) running on Cloudflare's edge network

* Cold start: &lt;1ms vs 100ms+ for containers
    
* Web APIs only (no `fs`, `process.env`)
    
* Each request is isolated and stateless
    

**Basic Structure**:

```typescript
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    // Your logic here
    return new Response('Hello World');
  }
}
```

# Request Handling

**Request Object**:

```typescript
const url = new URL(request.url);
const method = request.method; // GET, POST, etc.
const headers = request.headers;
const body = await request.json(); // or .text()

// Query parameters
const userId = url.searchParams.get('userId'); // ?userId=123
```

**Response Object**:

```typescript
// Simple text
return new Response('Hello');

// JSON with headers
return new Response(JSON.stringify({data: 'value'}), {
  status: 201,
  headers: { 'Content-Type': 'application/json' }
});

// Error response
return new Response(JSON.stringify({error: 'Bad request'}), {
  status: 400,
  headers: { 'Content-Type': 'application/json' }
});
```

# Basic Routing

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

    if (path === '/api/users' && method === 'GET') {
      return new Response(JSON.stringify([{id: 1, name: 'Alice'}]), {
        headers: { 'Content-Type': 'application/json' }
      });
    }

    if (path === '/api/users' && method === 'POST') {
      const userData = await request.json();
      // Validation
      if (!userData.name) {
        return new Response(JSON.stringify({error: 'Name required'}), {
          status: 400,
          headers: { 'Content-Type': 'application/json' }
        });
      }
      // Process...
      return new Response(JSON.stringify({id: 123, ...userData}), {
        status: 201,
        headers: { 'Content-Type': 'application/json' }
      });
    }

    return new Response('Not Found', { status: 404 });
  }
}
```

# Environment & Configuration

**wrangler.toml**:

```toml
name = "my-worker"
compatibility_date = "2024-01-01"

# Non-secret environment variables
[vars]
API_URL = "https://api.example.com"
DEBUG = "true"
```

**Secrets** (sensitive data):

```bash
# Set secrets via CLI
wrangler secret put API_KEY
wrangler secret put DATABASE_PASSWORD
```

**Using in code**:

```typescript
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const apiKey = env.API_KEY; // secret
    const apiUrl = env.API_URL; // regular env var
    
    return new Response(`Using ${apiUrl}`);
  }
}
```

**TypeScript setup**:

```bash
# Generate types for your specific Worker config
pnpm wrangler types
```

Add to `tsconfig.json`:

```json
{
  "compilerOptions": {
    "types": ["./worker-configuration.d.ts"]
  }
}
```

# External API Calls

```typescript
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    try {
      // Call external API
      const response = await fetch('https://api.example.com/data', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${env.API_TOKEN}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ query: 'example' })
      });

      if (!response.ok) {
        return new Response(JSON.stringify({error: 'External API failed'}), {
          status: 500,
          headers: { 'Content-Type': 'application/json' }
        });
      }

      const data = await response.json();
      
      // Transform and return
      return new Response(JSON.stringify({
        result: data.someField,
        timestamp: Date.now()
      }), {
        headers: { 'Content-Type': 'application/json' }
      });

    } catch (error) {
      return new Response(JSON.stringify({error: 'Request failed'}), {
        status: 500,
        headers: { 'Content-Type': 'application/json' }
      });
    }
  }
}
```

# Caching

**Cache API** (like a persistent Map&lt;string, Response&gt;):

```typescript
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const cache = caches.default; // or await caches.open('my-cache')
    const cacheKey = 'weather-london';

    // Try cache first
    const cachedResponse = await cache.match(cacheKey);
    if (cachedResponse) {
      return cachedResponse;
    }

    // Cache miss - fetch fresh data
    const freshResponse = await fetch(`https://api.weather.com?key=${env.API_KEY}`);
    const data = await freshResponse.json();
    
    const response = new Response(JSON.stringify(data), {
      headers: { 
        'Content-Type': 'application/json',
        'Cache-Control': 'max-age=300' // Browser caches for 5 min
      }
    });

    // Store in Cloudflare cache (background)
    ctx.waitUntil(cache.put(cacheKey, response.clone()));
    
    return response;
  }
}
```

**Key concepts**:

* `caches.default` = global cache, [`caches.open`](http://caches.open)`('name')` = named cache
    
* `response.clone()` = needed because Response can only be read once
    
* `ctx.waitUntil()` = keep Worker alive for background task, don't block response
    
* `Cache-Control` header = browser caching, separate from `caches` API
    

# Middleware Pattern

```typescript
// Middleware functions return Response to stop, null to continue
async function corsMiddleware(request: Request): Promise<Response | null> {
  if (request.method === 'OPTIONS') {
    return new Response(null, {
      headers: {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
        'Access-Control-Allow-Headers': 'Content-Type, Authorization'
      }
    });
  }
  return null;
}

async function authMiddleware(request: Request): Promise<Response | null> {
  const authHeader = request.headers.get('Authorization');
  if (!authHeader?.startsWith('Bearer ')) {
    return new Response(JSON.stringify({error: 'Unauthorized'}), {
      status: 401,
      headers: { 'Content-Type': 'application/json' }
    });
  }
  return null;
}

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    // Run middleware chain
    const middlewares = [corsMiddleware];
    for (const middleware of middlewares) {
      const response = await middleware(request);
      if (response) return response;
    }

    // Protected routes
    const url = new URL(request.url);
    if (url.pathname.startsWith('/api/protected/')) {
      const authResponse = await authMiddleware(request);
      if (authResponse) return authResponse;
    }

    // Your routes...
    return new Response('OK');
  }
}
```

# Streaming Responses

For large datasets or real-time data:

```typescript
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const stream = new ReadableStream({
      async start(controller) {
        for (let i = 0; i < 10000; i++) {
          // Each line is a separate JSON object
          const chunk = JSON.stringify({id: i, data: `Item ${i}`}) + '\n';
          controller.enqueue(new TextEncoder().encode(chunk));
          
          // Yield occasionally
          if (i % 100 === 0) {
            await new Promise(resolve => setTimeout(resolve, 0));
          }
        }
        controller.close();
      }
    });

    return new Response(stream, {
      headers: {
        'Content-Type': 'application/x-ndjson', // Newline-delimited JSON
        'Transfer-Encoding': 'chunked'
      }
    });
  }
}
```

# Production Patterns

**Error handling & logging**:

```typescript
export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const startTime = Date.now();
    
    try {
      // Your logic...
      const response = new Response(JSON.stringify({success: true}), {
        headers: { 'Content-Type': 'application/json' }
      });
      
      // Success metrics
      console.log(`Success: ${request.method} ${new URL(request.url).pathname} - ${Date.now() - startTime}ms`);
      return response;
      
    } catch (error) {
      console.error(`Error: ${request.method} ${request.url}`, error);
      return new Response(JSON.stringify({error: 'Internal Server Error'}), {
        status: 500,
        headers: { 'Content-Type': 'application/json' }
      });
    }
  }
}
```

**Simple rate limiting**:

```typescript
const rateLimits = new Map<string, {count: number; resetTime: number}>();

function isRateLimited(ip: string, limit: number, windowMs: number): boolean {
  const now = Date.now();
  const current = rateLimits.get(ip);
  
  // No data or window expired - reset
  if (!current || now > current.resetTime) {
    rateLimits.set(ip, {count: 1, resetTime: now + windowMs});
    return false;
  }
  
  // Hit limit
  if (current.count >= limit) {
    return true;
  }
  
  // Increment and allow
  current.count++;
  return false;
}
```

# Key Reminders

* **Stateless**: Variables don't persist between requests
    
* **Web APIs**: Use `fetch`, `Request`, `Response`, `URL` - not Node.js APIs
    
* **Secrets vs Env Vars**: Secrets via CLI, regular config in wrangler.toml
    
* **TypeScript**: Run `wrangler types` after config changes
    
* **Caching**: `caches` API for persistence, `ctx.waitUntil()` for background tasks
    
* **Rate Limiting**: Implement your own, resets on each deployment (use KV for persistence)
    

# Deployment

```bash
# Deploy to production
wrangler deploy

# Deploy to staging
wrangler deploy --env staging

# View logs
wrangler tail
```
