| name | cloudflare-worker-dev |
| description | Cloudflare Workers, KV, Durable Objects, and edge computing development. Use for serverless APIs, caching, rate limiting, real-time features. Activate on "Workers", "KV", "Durable Objects", "wrangler", "edge function", "Cloudflare". NOT for Cloudflare Pages configuration (use deployment docs), DNS management, or general CDN settings. |
| allowed-tools | Read,Write,Edit,Bash,Grep,Glob |
| metadata | {"category":"DevOps & Site Reliability","tags":["cloudflare","workers","edge-computing","serverless","kv","caching","rate-limiting"],"pairs-with":[{"skill":"devops-automator","reason":"CI/CD pipelines deploy and manage Cloudflare Worker deployments across environments"},{"skill":"caching-strategies","reason":"Workers KV and Cache API are key components of edge caching architectures"},{"skill":"modern-auth-2026","reason":"Workers often handle auth token validation and session management at the edge"},{"skill":"api-architect","reason":"Workers frequently serve as API gateways requiring proper REST/GraphQL design"}]} |
Cloudflare Workers Development
Build high-performance edge APIs with Workers, KV for caching, and Durable Objects for real-time coordination.
Core Architecture
When to Use What
| Service | Use Case | Characteristics |
|---|
| Workers | Request handling, API logic | Stateless, 50ms CPU (free), 30s (paid) |
| KV | Caching, config, sessions | Eventually consistent, fast reads |
| Durable Objects | Real-time, coordination | Strongly consistent, single-threaded |
| R2 | File storage | S3-compatible, no egress fees |
| D1 | SQLite at edge | Serverless SQL, good for reads |
Worker Fundamentals
Basic Worker Structure
export interface Env {
MEETING_CACHE: KVNamespace;
RATE_LIMIT: KVNamespace;
API_KEY: string;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
if (request.method === 'OPTIONS') {
return handleCORS();
}
try {
if (url.pathname === '/health') {
return json({ status: 'ok' });
}
if (url.pathname.startsWith('/api/')) {
return handleAPI(request, env, ctx);
}
return new Response('Not Found', { status: });
} (error) {
.(, error);
({ : }, );
}
},
() {
ctx.((env));
}
};
CORS Headers (Essential)
const CORS_HEADERS = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
};
function handleCORS(): Response {
return new Response(null, { status: 204, headers: CORS_HEADERS });
}
function json(data: unknown, status = 200): Response {
return new Response(JSON.stringify(data), {
status,
headers: {
...CORS_HEADERS,
'Content-Type': 'application/json',
},
});
}
wrangler.toml Configuration
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"
[[kv_namespaces]]
binding = "MEETING_CACHE"
id = "abc123..."
preview_id = "def456..."
[[kv_namespaces]]
binding = "RATE_LIMIT"
id = "ghi789..."
[vars]
CACHE_TTL = "86400"
RATE_LIMIT_REQUESTS = "100"
RATE_LIMIT_WINDOW = "3600"
[triggers]
crons = ["0 */6 * * *"]
KV Storage Patterns
Basic KV Operations
await env.CACHE.put('key', JSON.stringify(data), {
expirationTtl: 86400,
});
await env.CACHE.put('key', value, {
expirationTtl: 3600,
metadata: { createdAt: Date.now(), source: 'api' },
});
const value = await env.CACHE.get('key');
const parsed = await env.CACHE.get('key', 'json');
const { value, metadata } = await env.CACHE.getWithMetadata('key', 'json');
await env.CACHE.delete('key');
const { keys, cursor } = await env.CACHE.list({ prefix: });
Geohash-Based Caching
import Geohash from 'latlon-geohash';
function getCacheKey(lat: number, lng: number, radius: number): string {
const geohash = Geohash.encode(lat, lng, 3);
return `meetings:${geohash}:${radius}`;
}
async function getMeetingsWithCache(
lat: number,
lng: number,
radius: number,
env: Env
): Promise<{ data: Meeting[]; cached: boolean; geohash: string }> {
const geohash = Geohash.encode(lat, lng, 3);
const cacheKey = `meetings:${geohash}:${radius}`;
const cached = await env.MEETING_CACHE.(cacheKey, );
(cached) {
{ : cached, : , geohash };
}
data = (lat, lng, radius);
env..(
env..(cacheKey, .(data), {
: ,
: { : .(), geohash },
})
);
{ data, : , geohash };
}
Response Headers for Cache Debugging
function meetingsResponse(data: Meeting[], cached: boolean, geohash: string): Response {
return new Response(JSON.stringify(data), {
headers: {
...CORS_HEADERS,
'Content-Type': 'application/json',
'X-Cache': cached ? 'HIT' : 'MISS',
'X-Geohash': geohash,
'Cache-Control': 'public, max-age=3600',
},
});
}
Rate Limiting
IP-Based Rate Limiting
interface RateLimitConfig {
maxRequests: number;
windowSeconds: number;
}
async function checkRateLimit(
ip: string,
env: Env,
config: RateLimitConfig
): Promise<{ allowed: boolean; remaining: number; resetAt: number }> {
const key = `rate:${ip}`;
const now = Math.floor(Date.now() / 1000);
const windowStart = now - config.windowSeconds;
const stored = await env.RATE_LIMIT.get(key, 'json') as {
count: number;
windowStart: number;
} | null;
if (!stored || stored.windowStart < windowStart) {
await env.RATE_LIMIT.put(key, JSON.({
: ,
: now,
}), { : config. });
{
: ,
: config. - ,
: now + config.,
};
}
(stored. >= config.) {
{
: ,
: ,
: stored. + config.,
};
}
env..(key, .({
: stored. + ,
: stored.,
}), { : config. });
{
: ,
: config. - stored. - ,
: stored. + config.,
};
}
(): <> {
ip = request..() || ;
rateLimit = (ip, env, {
: (env. || ),
: (env. || ),
});
(!rateLimit.) {
({ : }, , {
: ,
: rateLimit..(),
});
}
}
Durable Objects (Real-Time)
Chat Room Example
export class ChatRoom {
state: DurableObjectState;
sessions: WebSocket[] = [];
constructor(state: DurableObjectState) {
this.state = state;
}
async fetch(request: Request): Promise<Response> {
const url = new URL(request.url);
if (url.pathname === '/websocket') {
if (request.headers.get('Upgrade') !== 'websocket') {
return new Response('Expected WebSocket', { status: 400 });
}
const [client, server] = Object.values(new WebSocketPair());
server.accept();
..(server);
server.(, {
.(event. , server);
});
server.(, {
. = ..( s !== server);
});
(, { : , : client });
}
(, { : });
}
() {
..( {
(session !== exclude && session. === .) {
session.(message);
}
});
}
}
{
() {
url = (request.);
(url..()) {
roomId = url..()[];
id = env..(roomId);
room = env..(id);
room.(request);
}
}
};
Deployment & Debugging
Commands
npx wrangler dev
npx wrangler dev --remote
npx wrangler deploy
npx wrangler deploy --env staging
npx wrangler secret put API_KEY
npx wrangler secret list
npx wrangler kv:key list --namespace-id=xxx
npx wrangler kv:key get --namespace-id=xxx "key"
npx wrangler kv:key delete --namespace-id=xxx "key"
npx wrangler tail
npx wrangler tail --format=pretty
Error Codes
| Code | Meaning |
|---|
| 1101 | Worker threw exception |
| 1102 | CPU time limit exceeded |
| 1015 | Rate limited by Cloudflare |
| 524 | Origin timeout (>100s) |
Quick Reference
const ip = request.headers.get('CF-Connecting-IP');
const country = request.cf?.country;
ctx.waitUntil(doBackgroundWork());
return new Response(readableStream, {
headers: { 'Content-Type': 'text/event-stream' }
});
const response = await fetch(upstreamUrl, request);
return new Response(response.body, response);
Anti-Patterns
❌ Awaiting KV writes in hot path
async function handler(request: Request, env: Env) {
const data = await fetchData();
await env.CACHE.put('key', data);
return json(data);
}
async function handler(request: Request, env: Env, ctx: ExecutionContext) {
const data = await fetchData();
ctx.waitUntil(env.CACHE.put('key', data));
return json(data);
}
❌ Missing CORS handling
export default {
async fetch(request: Request) {
return json({ data: 'hello' });
}
}
export default {
async fetch(request: Request) {
if (request.method === 'OPTIONS') {
return new Response(null, { status: 204, headers: CORS_HEADERS });
}
return json({ data: 'hello' });
}
}
❌ Secrets in wrangler.toml
[vars]
API_KEY = "sk-live-xxxxx"
❌ Ignoring KV eventual consistency
await env.KV.put('count', String(newCount));
const verify = await env.KV.get('count');
await env.KV.put('count', String(newCount));
return json({ count: newCount });
❌ Blocking on external APIs without timeout
const data = await fetch('https://slow-api.com/data');
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
try {
const data = await fetch('https://slow-api.com/data', {
signal: controller.signal
});
} finally {
clearTimeout(timeout);
}
References
See /references/ for detailed guides:
kv-patterns.md - Advanced KV usage patterns
durable-objects.md - Real-time features with DO
debugging.md - Troubleshooting common issues