- name
- infrastructure-cloudflare-workers
- description
- Building serverless APIs at the edge (low latency globally)
# Cloudflare Workers
**Scope**: Edge computing with Cloudflare Workers - Edge functions, KV storage, Durable Objects, R2
**Lines**: 365
**Last Updated**: 2025-10-18
**Format Version**: 1.0 (Atomic)
---
## When to Use This Skill
**Activate when**:
- Building serverless APIs at the edge (low latency globally)
- Implementing CDN logic and custom routing
- A/B testing and feature flags
- Authentication and authorization at the edge
- API proxying and transformation
- Static site generation with dynamic elements
**Prerequisites**:
- Cloudflare account (free tier available)
- Node.js installed for Wrangler CLI
- Wrangler CLI installed (`npm install -g wrangler`)
- Basic JavaScript/TypeScript knowledge
- Cloudflare domain (for production deployment)
**Common scenarios**:
- API endpoints with global low latency
- Image resizing and optimization
- Request/response manipulation
- Rate limiting and bot protection
- Geo-routing and localization
- Edge-side rendering (ESR)
---
## Core Concepts
### 1. Basic Worker
```javascript
// src/index.js - Simple worker
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
// Simple routing
if (url.pathname === '/api/hello') {
return new Response(JSON.stringify({
message: 'Hello from the edge!',
location: request.cf.city,
country: request.cf.country
}), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
});
}
// 404 response
return new Response('Not Found', { status: 404 });
}
};
```
### 2. Wrangler Configuration
```toml
# wrangler.toml
name = "my-worker"
main = "src/index.js"
compatibility_date = "2024-01-01"
# Environment variables
[vars]
ENVIRONMENT = "production"
API_VERSION = "v1"
# KV Namespace bindings
[[kv_namespaces]]
binding = "CACHE"
id = "abcdef1234567890abcdef1234567890"
# Durable Object bindings
[[durable_objects.bindings]]
name = "COUNTER"
class_name = "Counter"
# R2 bucket bindings
[[r2_buckets]]
binding = "ASSETS"
bucket_name = "my-assets"
# Secrets (set with: wrangler secret put SECRET_NAME)
# Access via env.SECRET_NAME
# Routes (custom domain)
[[routes]]
pattern = "api.example.com/*"
zone_name = "example.com"
# Workers for Platforms (optional)
[dispatch_namespaces]
binding = "DISPATCHER"
namespace = "my-namespace"
```
### 3. Request/Response Handling
```typescript
// src/index.ts - TypeScript worker
interface Env {
CACHE: KVNamespace;
API_KEY: string;
ENVIRONMENT: string;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
// CORS handling
if (request.method === 'OPTIONS') {
return handleCORS();
}
// Authentication
const apiKey = request.headers.get('X-API-Key');
if (apiKey !== env.API_KEY) {
return new Response('Unauthorized', { status: 401 });
}
// Route handling
switch (url.pathname) {
case '/api/data':
return handleData(request, env, ctx);
case '/api/upload':
return handleUpload(request, env);
default:
return new Response('Not Found', { status: 404 });
}
}
};
function handleCORS(): Response {
return new Response(null, {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, X-API-Key',
'Access-Control-Max-Age': '86400',
}
});
}
async function handleData(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const cacheKey = new URL(request.url).pathname;
// Check cache first
const cached = await env.CACHE.get(cacheKey, 'json');
if (cached) {
return Response.json(cached, {
headers: { 'X-Cache': 'HIT' }
});
}
// Fetch data
const data = await fetchData();
// Cache in background
ctx.waitUntil(
env.CACHE.put(cacheKey, JSON.stringify(data), {
expirationTtl: 3600 // 1 hour
})
);
return Response.json(data, {
headers: { 'X-Cache': 'MISS' }
});
}
async function fetchData() {
// Fetch from origin API
const response = await fetch('https://api.example.com/data');
return response.json();
}
```
### 4. KV Storage
```typescript
// KV operations
interface Env {
CACHE: KVNamespace;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const key = url.searchParams.get('key');
if (request.method === 'GET') {
// Get value
const value = await env.CACHE.get(key);
if (!value) {
return new Response('Not Found', { status: 404 });
}
return new Response(value);
}
if (request.method === 'POST') {
// Put value with expiration
const value = await request.text();
await env.CACHE.put(key, value, {
expirationTtl: 3600, // Seconds
metadata: { timestamp: Date.now() }
});
return new Response('Stored', { status: 201 });
}
if (request.method === 'DELETE') {
// Delete value
await env.CACHE.delete(key);
return new Response('Deleted', { status: 204 });
}
// List keys with prefix
if (url.pathname === '/list') {
const prefix = url.searchParams.get('prefix') || '';
const list = await env.CACHE.list({ prefix, limit: 100 });
return Response.json(list.keys);
}
return new Response('Method Not Allowed', { status: 405 });
}
};
```
### 5. Durable Objects
```typescript
// src/counter.ts - Durable Object
export class Counter {
state: DurableObjectState;
value: number;
constructor(state: DurableObjectState, env: Env) {
this.state = state;
this.value = 0;
}
async initialize() {
// Load state from storage
const stored = await this.state.storage.get('value');
this.value = stored || 0;
}
async fetch(request: Request): Promise<Response> {
await this.initialize();
const url = new URL(request.url);
if (url.pathname === '/increment') {
this.value++;
await this.state.storage.put('value', this.value);
return Response.json({ value: this.value });
}
if (url.pathname === '/decrement') {
this.value--;
await this.state.storage.put('value', this.value);
return Response.json({ value: this.value });
}
if (url.pathname === '/get') {
return Response.json({ value: this.value });
}
return new Response('Not Found', { status: 404 });
}
}
// src/index.ts - Use Durable Object
export { Counter } from './counter';
interface Env {
COUNTER: DurableObjectNamespace;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Get Durable Object instance by ID
const id = env.COUNTER.idFromName('global-counter');
const stub = env.COUNTER.get(id);
// Forward request to Durable Object
return stub.fetch(request);
}
};
```
### 6. R2 Storage
```typescript
// R2 bucket operations
interface Env {
ASSETS: R2Bucket;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const key = url.pathname.slice(1); // Remove leading /
if (request.method === 'GET') {
// Get object
const object = await env.ASSETS.get(key);
if (!object) {
return new Response('Not Found', { status: 404 });
}
const headers = new Headers();
object.writeHttpMetadata(headers);
headers.set('etag', object.httpEtag);
return new Response(object.body, { headers });
}
if (request.method === 'PUT') {
// Upload object
await env.ASSETS.put(key, request.body, {
httpMetadata: {
contentType: request.headers.get('content-type') || 'application/octet-stream',
},
customMetadata: {
uploadedBy: 'worker',
timestamp: new Date().toISOString(),
}
});
return new Response('Uploaded', { status: 201 });
}
if (request.method === 'DELETE') {
// Delete object
await env.ASSETS.delete(key);
return new Response('Deleted', { status: 204 });
}
return new Response('Method Not Allowed', { status: 405 });
}
};
```
---
## Patterns
### Edge Caching
```typescript
// Cache API with edge caching
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const cache = caches.default;
// Try to get from cache
let response = await cache.match(request);
if (response) {
return response;
}
// Fetch from origin
response = await fetch(request);
// Cache successful responses
if (response.ok) {
const responseToCache = response.clone();
ctx.waitUntil(cache.put(request, responseToCache));
}
return response;
}
};
```
### Request Rewriting
```typescript
// Rewrite requests to different origins
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
// API proxy
if (url.pathname.startsWith('/api/')) {
url.hostname = 'api.backend.com';
url.pathname = url.pathname.replace('/api/', '/v1/');
const modifiedRequest = new Request(url.toString(), {
method: request.method,
headers: request.headers,
body: request.body,
});
return fetch(modifiedRequest);
}
// Default to static assets
url.hostname = 'static.example.com';
return fetch(url.toString());
}
};
```
### A/B Testing
```typescript
// A/B testing with cookie-based variants
export default {
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
// Check for existing variant cookie
let variant = getCookie(request, 'ab_variant');
if (!variant) {
// Assign random variant
variant = Math.random() < 0.5 ? 'A' : 'B';
}
// Fetch variant-specific content
const response = await fetch(`https://origin.com/${variant}${url.pathname}`);
// Set variant cookie
const newResponse = new Response(response.body, response);
newResponse.headers.set('Set-Cookie', `ab_variant=${variant}; Path=/; Max-Age=86400`);
return newResponse;
}
};
function getCookie(request: Request, name: string): string | null {
const cookies = request.headers.get('Cookie');
if (!cookies) return null;
const cookie = cookies.split(';').find(c => c.trim().startsWith(`${name}=`));
Auf GitHub ansehen