| name | cloudflare-workers |
| description | Build and deploy edge functions with Cloudflare Workers and Wrangler. Use for APIs, cron jobs, and edge middleware. |
| license | MIT |
| metadata | {"author":"devops-skills","version":"1.0"} |
Cloudflare Workers
Deploy JavaScript and TypeScript functions to Cloudflare's global edge network with sub-millisecond cold starts.
When to Use
- Building lightweight APIs and microservices at the edge.
- Adding middleware (auth, rate limiting, header injection) in front of origin servers.
- Running cron jobs on a schedule without maintaining infrastructure.
- Processing webhooks, image transformations, or A/B testing logic.
- Serving dynamic content from KV, D1, or R2 storage bindings.
Prerequisites
- Node.js 18+ installed locally.
- Wrangler CLI:
npm install -g wrangler.
- Cloudflare account (free plan supports 100,000 requests/day).
- Authenticated:
wrangler login or set CLOUDFLARE_API_TOKEN.
Quick Start
npm create cloudflare@latest my-worker
cd my-worker
npx wrangler login
npx wrangler dev
npx wrangler deploy
Essential Wrangler Commands
npx wrangler dev --remote
npx wrangler deploy --env staging
npx wrangler secret put API_TOKEN
npx wrangler secret put API_TOKEN --env staging
npx wrangler secret list
npx wrangler tail
npx wrangler tail --status=error --search="timeout"
npx wrangler deployments list
npx wrangler rollback
Wrangler Configuration
name = "my-api"
main = "src/index.ts"
compatibility_date = "2024-09-01"
compatibility_flags = ["nodejs_compat"]
routes = [
{ pattern = "api.example.com/*", zone_name = "example.com" }
]
[vars]
ENVIRONMENT = "production"
API_VERSION = "v2"
[env.staging]
name = "my-api-staging"
routes = [
{ pattern = "api-staging.example.com/*", zone_name = "example.com" }
]
[env.staging.vars]
ENVIRONMENT = "staging"
Worker Examples
Basic API Router
export interface Env {
ENVIRONMENT: string;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
switch (url.pathname) {
case "/":
return new Response("OK", { status: 200 });
case "/api/health":
return Response.json({
status: "healthy",
env: env.ENVIRONMENT,
timestamp: new Date().toISOString(),
});
case "/api/data":
if (request.method !== "POST") {
return new Response(, { : });
}
body = request.();
ctx.((body));
.({ : });
:
(, { : });
}
},
};
(): <> {
(, {
: ,
: .(data),
: { : },
});
}
Middleware: Rate Limiting with KV
interface Env {
RATE_LIMIT_KV: KVNamespace;
ORIGIN_URL: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const ip = request.headers.get("CF-Connecting-IP") || "unknown";
const key = `ratelimit:${ip}`;
const window = 60;
const maxRequests = 100;
const current = parseInt((await env.RATE_LIMIT_KV.get(key)) || "0");
if (current >= maxRequests) {
return new Response("Too Many Requests", {
status: 429,
headers: { "Retry-After": String(window) },
});
}
await env.RATE_LIMIT_KV.put(key, String(current + ), {
: ,
});
originRequest = (env. + (request.)., request);
(originRequest);
},
};
KV Storage Binding
[[kv_namespaces]]
binding = "MY_KV"
id = "abc123def456"
[[kv_namespaces]]
binding = "MY_KV"
id = "abc123def456"
preview_id = "preview789"
interface Env {
MY_KV: KVNamespace;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
await env.MY_KV.put("session:abc", JSON.stringify({ user: "alice" }), {
expirationTtl: 3600,
});
const session = await env.MY_KV.get("session:abc", "json");
const list = await env.MY_KV.list({ prefix: "session:", limit: 100 });
await env.MY_KV.delete("session:abc");
return Response.json({ session, keys: list.keys. });
},
};
npx wrangler kv namespace create MY_KV
npx wrangler kv namespace list
npx wrangler kv key put --namespace-id=abc123 "config:feature-flags" '{"darkMode":true}'
npx wrangler kv key get --namespace-id=abc123 "config:feature-flags"
npx wrangler kv key list --namespace-id=abc123 --prefix="config:"
D1 Database Binding
[[d1_databases]]
binding = "DB"
database_name = "my-app"
database_id = "xxxx-yyyy-zzzz"
interface Env {
DB: D1Database;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const { results } = await env.DB.prepare(
"SELECT id, name, email FROM users WHERE active = ? LIMIT ?"
)
.bind(1, 50)
.all();
await env.DB.prepare("INSERT INTO users (name, email) VALUES (?, ?)")
.bind("Alice", "alice@example.com")
.run();
await env.DB.batch([
env.DB.prepare("UPDATE users SET active = 0 WHERE last_login < ?").bind("2024-01-01"),
env.DB.prepare("DELETE FROM sessions WHERE expires_at < ?").bind(Date.now()),
]);
.(results);
},
};
npx wrangler d1 create my-app
npx wrangler d1 list
npx wrangler d1 execute my-app --command="CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT, active INTEGER DEFAULT 1)"
npx wrangler d1 execute my-app --file=./migrations/001_init.sql
npx wrangler d1 execute my-app --command="SELECT * FROM users" --json
Cron Triggers
[triggers]
crons = [
"0 */6 * * *",
"0 0 * * MON",
"*/15 * * * *",
]
export default {
async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise<void> {
switch (event.cron) {
case "0 */6 * * *":
ctx.waitUntil(cleanupExpiredSessions(env));
break;
case "0 0 * * MON":
ctx.waitUntil(generateWeeklyReport(env));
break;
}
},
async fetch(request: Request, env: Env): Promise<Response> {
return new Response("OK");
},
};
Durable Objects
[durable_objects]
bindings = [
{ name = "COUNTER", class_name = "Counter" }
]
[[migrations]]
tag = "v1"
new_classes = ["Counter"]
export class Counter {
state: DurableObjectState;
constructor(state: DurableObjectState) {
this.state = state;
}
async fetch(request: Request): Promise<Response> {
let count = (await this.state.storage.get<number>("count")) || 0;
count++;
await this.state.storage.put("count", count);
return Response.json({ count });
}
}
interface Env {
COUNTER: DurableObjectNamespace;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const id = env..();
stub = env..(id);
stub.(request);
},
};
Custom Routing
routes = [
{ pattern = "api.example.com/v1/*", zone_name = "example.com" },
{ pattern = "api.example.com/v2/*", zone_name = "example.com" },
]
Troubleshooting
| Symptom | Cause | Fix |
|---|
Error 1101: Worker threw exception | Unhandled error in fetch handler | Wrap handler in try/catch; check wrangler tail for stack trace |
exceeded CPU time limit | Worker exceeds 10ms CPU (free) or 30s (paid) | Optimize code; offload work with ctx.waitUntil() |
| KV reads return stale data | KV is eventually consistent (~60s) | Use cacheTtl option or switch to Durable Objects for strong consistency |
wrangler dev binding errors | Local bindings not configured | Use --remote flag or configure preview_id in wrangler.toml |
| Secret not found in Worker | Secret set for wrong environment | Verify with wrangler secret list --env <env> |
| CORS errors from browser | Missing CORS headers in response | Add Access-Control-Allow-Origin headers; handle OPTIONS preflight |
| Route not matching | Pattern does not include /* suffix | Add /* to catch all paths: api.example.com/* |
Related Skills