cloudflare-workers
Cloudflare Workers
Setup
npm install -g wrangler && wrangler login
wrangler init my-worker
wrangler.toml
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"
[vars]
ENVIRONMENT = "production"
[[kv_namespaces]]
binding = "MY_KV"
id = "abc123"
[[d1_databases]]
binding = "DB"
database_name = "my-db"
database_id = "def456"
[[r2_buckets]]
binding = "BUCKET"
bucket_name = "my-bucket"
Worker Basics
interface Env {
MY_KV: KVNamespace; DB: D1Database; BUCKET: R2Bucket;
ENVIRONMENT: string; API_KEY: string;
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
if (url.pathname === "/api/health") return Response.json({ status: "ok" });
if (request.method === "POST" && url.pathname === "/api/data") {
const body = await request.json();
return new Response("Created", { status: 201 });
}
return new Response("Not Found", { status: 404 });
},
};
Wrangler CLI
wrangler dev # local dev server on localhost:8787
wrangler dev --remote # dev against real Cloudflare infrastructure
wrangler deploy # deploy to production
wrangler tail # stream live logs from deployed worker
wrangler secret put API_KEY # set an encrypted secret
wrangler secret list # list configured secrets
wrangler delete # remove the deployed worker
Routing
Manual routing with a map, or use hono/itty-router for path params:
// Manual
const routes: Record<string, () => Promise<Response>> = {
"/api/users": () => handleUsers(request),
"/api/posts": () => handlePosts(request),
};
const handler = routes[new URL(request.url).pathname];
if (handler) return handler();
// Hono (recommended for complex routing)
import { Hono } from "hono";
const app = new Hono<{ Bindings: Env }>();
app.get("/users/:id", (c) => c.json({ id: c.req.param("id") }));
export default app;
KV Store
Global, low-latency key-value store. Eventually consistent. Best for read-heavy data.
wrangler kv namespace create MY_KV
wrangler kv namespace create MY_KV --preview
await env.MY_KV.put("user:123", JSON.stringify({ name: "Alice" }), {
expirationTtl: 3600, metadata: { created: Date.now() },
});
const value = await env.MY_KV.get("user:123", "json");
const list = await env.MY_KV.list({ prefix: "user:", limit: 100 });
await env.MY_KV.delete("user:123");
D1 Database
SQLite at the edge with queries, transactions, and migrations.
wrangler d1 create my-db
wrangler d1 execute my-db --command "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)"
wrangler d1 migrations create my-db init
wrangler d1 migrations apply my-db
const { results } = await env.DB.prepare("SELECT * FROM users WHERE id = ?").bind(userId).all();
await env.DB.prepare("INSERT INTO users (name) VALUES (?)").bind("Alice").run();
await env.DB.batch([
env.DB.prepare("INSERT INTO users (name) VALUES (?)").bind("Bob"),
env.DB.prepare("INSERT INTO users (name) VALUES (?)").bind("Carol"),
]);
const user = await env.DB.prepare("SELECT * FROM users WHERE id = ?").bind(1).first();
R2 Storage
S3-compatible object storage with no egress fees.
wrangler r2 bucket create my-bucket
// Upload
await env.BUCKET.put("images/photo.jpg", imageData, {
httpMetadata: { contentType: "image/jpeg" },
});
// Download
const object = await env.BUCKET.get("images/photo.jpg");
if (object) {
return new Response(object.body, {
headers: { "Content-Type": object.httpMetadata?.contentType ?? "application/octet-stream" },
});
}
// List, delete
const listed = await env.BUCKET.list({ prefix: "images/", limit: 50 });
await env.BUCKET.delete("images/photo.jpg");
For large files use createMultipartUpload(), uploadPart(), complete().
Durable Objects
Strongly consistent, stateful compute. Each object has a unique ID and private storage. Use for rate limiters, WebSocket coordination, collaborative editing, session state.
[[durable_objects.bindings]]
name = "COUNTER"
class_name = "Counter"
[[migrations]]
tag = "v1"
new_classes = ["Counter"]
export class Counter implements DurableObject {
constructor(private state: DurableObjectState, private env: Env) {}
async fetch(request: Request): Promise<Response> {
const current = (await this.state.storage.get<number>("count")) ?? 0;
await this.state.storage.put("count", current + 1);
return Response.json({ count: current + 1 });
}
}
// Calling from a worker:
const id = env.COUNTER.idFromName("my-counter");
const response = await env.COUNTER.get(id).fetch(request);
Workers AI
Run ML models at the edge. Add [ai] with binding = "AI" to wrangler.toml.
// Text generation
const resp = await env.AI.run("@cf/meta/llama-3.1-8b-instruct", {
messages: [{ role: "user", content: "Summarize this article." }],
});
// Embeddings
const emb = await env.AI.run("@cf/baai/bge-base-en-v1.5", { text: ["document to embed"] });
// Image classification
const cls = await env.AI.run("@cf/microsoft/resnet-50", { image: await request.arrayBuffer() });
Environment Variables and Secrets
Non-sensitive values go in wrangler.toml under [vars]. Set secrets with wrangler secret put API_KEY. Both accessed through env.API_KEY, env.ENVIRONMENT, etc.
Multiple environments:
[env.staging]
name = "my-worker-staging"
vars = { ENVIRONMENT = "staging" }
[env.production]
name = "my-worker-production"
vars = { ENVIRONMENT = "production" }
Deploy with wrangler deploy --env staging or --env production.
Cron Triggers
[triggers]
crons = ["0 */6 * * *", "30 8 * * 1"]
export default {
async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext): Promise<void> {
ctx.waitUntil(doCleanup(env));
},
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
return new Response("OK");
},
};
Test locally: curl "http://localhost:8787/__scheduled?cron=0+*/6+*+*+*"
Middleware Patterns
CORS
function corsHeaders(origin: string): HeadersInit {
return {
"Access-Control-Allow-Origin": origin,
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
};
}
Auth
async function requireAuth(request: Request, env: Env): Promise<Response | null> {
const token = request.headers.get("Authorization")?.replace("Bearer ", "");
if (!token || token !== env.API_KEY) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
return null; // proceed
}
Rate Limiting
Use a Durable Object to track request timestamps per key. Store timestamps in storage, filter to the current window, reject if over limit, append and persist otherwise.
Local Development
wrangler dev # local server with miniflare runtime
wrangler dev --persist-to=./data # persist KV/D1/R2 data locally
wrangler dev --port 3000 # custom port
wrangler dev --remote # proxy to Cloudflare (real bindings)
Miniflare simulates KV, D1, R2, Durable Objects, and caches locally.
Deployment
wrangler deploy # deploy to production
wrangler deploy --env staging # named environment
wrangler deploy --dry-run # validate without deploying
wrangler versions list # list deployed versions
wrangler rollback # rollback to previous version
Custom domains:
routes = [{ pattern = "api.example.com/*", zone_name = "example.com" }]
Common Patterns
API Proxy
async function proxyRequest(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
url.hostname = "api.upstream.com";
return fetch(new Request(url.toString(), {
method: request.method,
headers: { ...Object.fromEntries(request.headers), "X-API-Key": env.UPSTREAM_KEY },
body: request.body,
}));
}
Edge Cache
async function cachedFetch(request: Request, ctx: ExecutionContext): Promise<Response> {
const cache = caches.default;
let response = await cache.match(request);
if (response) return response;
response = await fetch("https://api.origin.com" + new URL(request.url).pathname);
const cached = new Response(response.body, response);
cached.headers.set("Cache-Control", "s-maxage=300");
ctx.waitUntil(cache.put(request, cached.clone()));
return cached;
}
Webhook Handler
async function handleWebhook(request: Request, env: Env): Promise<Response> {
const signature = request.headers.get("X-Signature-256") ?? "";
const body = await request.text();
const key = await crypto.subtle.importKey(
"raw", new TextEncoder().encode(env.WEBHOOK_SECRET),
{ name: "HMAC", hash: "SHA-256" }, false, ["verify"]
);
const valid = await crypto.subtle.verify(
"HMAC", key, hexToBytes(signature.replace("sha256=", "")),
new TextEncoder().encode(body)
);
if (!valid) return new Response("Invalid signature", { status: 401 });
return new Response("OK", { status: 200 });
}
URL Shortener
app.post("/shorten", async (c) => {
const { url } = await c.req.json();
const id = crypto.randomUUID().slice(0, 8);
await c.env.MY_KV.put(`url:${id}`, url, { expirationTtl: 86400 * 30 });
return c.json({ short: `${new URL(c.req.url).origin}/${id}` });
});
app.get("/:id", async (c) => {
const target = await c.env.MY_KV.get(`url:${c.req.param("id")}`);
if (!target) return c.text("Not found", 404);
return c.redirect(target, 302);
});
Limits
| Resource | Free | Paid |
|---|---|---|
| CPU time/request | 10 ms | 30 s (Unbound) / 50 ms |
| Memory | 128 MB | 128 MB |
| Worker size | 1 MB | 10 MB |
| Subrequests (fetch) | 50 | 1000 |
| KV reads/day | 100K | 10M+ |
| KV writes/day | 1K | 1M+ |
| D1 rows read/day | 5M | 50B |
| D1 rows written/day | 100K | 50M |
| R2 Class A ops/month | 1M | $4.50/M |
| R2 storage | 10 GB | $0.015/GB-mo |
| Cron triggers | 3 | 3+ |
| Request body size | 100 MB | 100 MB |
Key constraints: no raw TCP/UDP sockets (use WebSockets or Tunnels). crypto.subtle available. Node.js built-ins partially supported via nodejs_compat flag. Globals persist within an isolate but not across cold starts. ctx.waitUntil() extends execution after response for background work.
More from 1mangesh1/dev-skills-collection
curl-http
HTTP request construction and API testing with curl and HTTPie. Use when user asks to "test API", "make HTTP request", "curl POST", "send request", "test endpoint", "debug API", "upload file", "check response time", "set auth header", "basic auth with curl", "send JSON", "test webhook", "check status code", "follow redirects", "rate limit testing", "measure API latency", "stress test endpoint", "mock API response", or any HTTP calls from the command line.
28database-indexing
Database indexing internals, index type selection, query plan analysis, and write-overhead tradeoffs across PostgreSQL, MySQL, and MongoDB. Use when user asks to "optimize queries", "create indexes", "fix slow queries", "read EXPLAIN output", "reduce query time", "index strategy", "database performance", "composite index", "covering index", "partial index", "index bloat", "unused indexes", or needs help diagnosing and resolving database performance problems.
13testing-strategies
Testing strategies, patterns, and methodologies across the full testing spectrum. Use when asked about unit tests, integration tests, e2e tests, test pyramid, mocking, test doubles, TDD, property-based testing, snapshot testing, test coverage, mutation testing, contract testing, performance testing, test data management, CI/CD testing, flaky tests, test anti-patterns, test organization, test isolation, test fixtures, test parameterization, or any testing strategy, approach, or methodology.
10secret-scanner
This skill should be used when the user asks to "scan for secrets", "find API keys", "detect credentials", "check for hardcoded passwords", "find leaked tokens", "scan for sensitive keys", "check git history for secrets", "audit repository for credentials", or mentions secret detection, credential scanning, API key exposure, token leakage, password detection, or security key auditing.
10terraform
Terraform infrastructure as code for provisioning, modules, state management, and workspaces. Use when user asks to "create infrastructure", "write Terraform", "manage state", "create module", "import resource", "plan changes", or any IaC tasks.
10kubernetes
Kubernetes and kubectl mastery for deployments, services, pods, debugging, and cluster management. Use when user asks to "deploy to k8s", "create deployment", "debug pod", "kubectl commands", "scale service", "check pod logs", "create ingress", or any Kubernetes tasks.
10