--- id: BagelHole/DevOps-Security-Agent-Skills/cloudflare-workers version: "7fa306f6" license: MIT install: manual updated: 2026-05-22 --- # cloudflare-workers — This skill equips you to build and deploy serverless functions directly on Cloudflare's global edge infrastructure using JavaScript or TypeScript. Execute compute workloads with minimal latency by leveraging Cloudflare's distributed network, ideal for APIs, middleware, and real-time request handling. Publisher: BagelHole · Stars: 44 · Updated: 2026-05-22 Install (manual): `git clone https://github.com/BagelHole/DevOps-Security-Agent-Skills` ## SKILL.md # 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 ```bash # Scaffold a new Worker project npm create cloudflare@latest my-worker cd my-worker # Login to Cloudflare npx wrangler login # Start local development server (port 8787) npx wrangler dev # Deploy to production npx wrangler deploy ``` ## Essential Wrangler Commands ```bash # Local development with remote bindings (KV, D1, R2) npx wrangler dev --remote # Deploy to a specific environment npx wrangler deploy --env staging # Set a secret (prompts for value) npx wrangler secret put API_TOKEN npx wrangler secret put API_TOKEN --env staging # List secrets npx wrangler secret list # Tail production logs in real time npx wrangler tail # Tail with filters npx wrangler tail --status=error --search="timeout" # View deployment versions npx wrangler deployments list # Rollback to a previous deployment npx wrangler rollback ``` ## Wrangler Configuration ```toml # wrangler.toml name = "my-api" main = "src/index.ts" compatibility_date = "2024-09-01" compatibility_flags = ["nodejs_compat"] # Custom routes routes = [ { pattern = "api.example.com/*", zone_name = "example.com" } ] # Or use a workers.dev subdomain # workers_dev = true # Environment variables (non-secret) [vars] ENVIRONMENT = "production" API_VERSION = "v2" # Staging environment override [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 ```typescript // src/index.ts export interface Env { ENVIRONMENT: string; } export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { 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("Method Not Allowed", { status: 405 }); } const body = await request.json(); // Process in the background after returning response ctx.waitUntil(logToAnalytics(body)); return Response.json({ received: true }); default: return new Response("Not Found", { status: 404 }); } }, }; async function logToAnalytics(data: unknown): Promise { await fetch("https://analytics.example.com/ingest", { method: "POST", body: JSON.stringify(data), headers: { "Content-Type": "application/json" }, }); } ``` ### Middleware: Rate Limiting with KV ```typescript // src/rate-limiter.ts interface Env { RATE_LIMIT_KV: KVNamespace; ORIGIN_URL: string; } export default { async fetch(request: Request, env: Env): Promise { const ip = request.headers.get("CF-Connecting-IP") || "unknown"; const key = `ratelimit:${ip}`; const window = 60; // seconds 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 + 1), { expirationTtl: window, }); // Forward to origin const originRequest = new Request(env.ORIGIN_URL + new URL(request.url).pathname, request); return fetch(originRequest); }, }; ``` ## KV Storage Binding ```toml # wrangler.toml [[kv_namespaces]] binding = "MY_KV" id = "abc123def456" # Preview namespace for local dev [[kv_namespaces]] binding = "MY_KV" id = "abc123def456" preview_id = "preview789" ``` ```typescript // KV operations in a Worker interface Env { MY_KV: KVNamespace; } export default { async fetch(request: Request, env: Env): Promise { // Write with TTL await env.MY_KV.put("session:abc", JSON.stringify({ user: "alice" }), { expirationTtl: 3600, }); // Read const session = await env.MY_KV.get("session:abc", "json"); // List keys by prefix const list = await env.MY_KV.list({ prefix: "session:", limit: 100 }); // Delete await env.MY_KV.delete("session:abc"); return Response.json({ session, keys: list.keys.length }); }, }; ``` ```bash # KV CLI operations 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 ```toml # wrangler.toml [[d1_databases]] binding = "DB" database_name = "my-app" database_id = "xxxx-yyyy-zzzz" ``` ```typescript // D1 SQL queries in a Worker interface Env { DB: D1Database; } export default { async fetch(request: Request, env: Env): Promise { // Parameterized query const { results } = await env.DB.prepare( "SELECT id, name, email FROM users WHERE active = ? LIMIT ?" ) .bind(1, 50) .all(); // Insert await env.DB.prepare("INSERT INTO users (name, email) VALUES (?, ?)") .bind("Alice", "alice@example.com") .run(); // Batch multiple statements 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()), ]); return Response.json(results); }, }; ``` ```bash # D1 CLI operations 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 ```toml # wrangler.toml [triggers] crons = [ "0 */6 * * *", # Every 6 hours "0 0 * * MON", # Every Monday at midnight "*/15 * * * *", # Every 15 minutes ] ``` ```typescript // src/index.ts — scheduled handler export default { async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise { 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 { return new Response("OK"); }, }; ``` ## Durable Objects ```toml # wrangler.toml [durable_objects] bindings = [ { name = "COUNTER", class_name = "Counter" } ] [[migrations]] tag = "v1" new_classes = ["Counter"] ``` ```typescript // src/counter.ts — Durable Object class export class Counter { state: DurableObjectState; constructor(state: DurableObjectState) { this.state = state; } async fetch(request: Request): Promise { let count = (await this.state.storage.get("count")) || 0; count++; await this.state.storage.put("count", count); return Response.json({ count }); } } // src/index.ts — route to Durable Object interface Env { COUNTER: DurableObjectNamespace; } export default { async fetch(request: Request, env: Env): Promise { const id = env.COUNTER.idFromName("global-counter"); const stub = env.COUNTER.get(id); return stub.fetch(request); }, }; ``` ## Custom Routing ```toml # Route to specific zones routes = [ { pattern = "api.example.com/v1/*", zone_name = "example.com" }, { pattern = "api.example.com/v2/*", zone_name = "example.com" }, ] # Or use custom domains (automatic SSL) # Dashboard: Workers > your-worker > Triggers > Custom Domains ``` ## 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 ` | | 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 - [cloudflare-pages](../cloudflare-pages/) - Frontend deployments with Pages Functions - [cloudflare-r2](../cloudflare-r2/) - Object storage at the edge - [cloudflare-zero-trust](../cloudflare-zero-trust/) - Protect Worker endpoints with Access [View on SkillFed](https://skillfed.io/BagelHole/DevOps-Security-Agent-Skills/cloudflare-workers) · [View on GitHub](https://github.com/BagelHole/DevOps-Security-Agent-Skills)