Cloudflare Workers run at the edge with 0ms cold starts and 100,000 free requests per day. Compared to AWS Lambda (cold starts of 100-1000ms, complex IAM, regional) or Vercel functions (limited free tier), Workers are the cleanest option for low-latency global APIs. Here's a 15-minute tutorial from zero to deployed.
Prerequisites
- Node 18+
- A Cloudflare account (free tier is fine)
- A domain on Cloudflare DNS (optional, for custom domain)
Step 1: Wrangler init
npm install -g wrangler
wrangler login
wrangler init my-api
cd my-api
Pick: Hello World example, TypeScript, no git init (you'll set up your own), no deploy yet.
Step 2: The fetch handler
src/index.ts:
export interface Env {
KV: KVNamespace;
DB: D1Database;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
if (url.pathname === "/health") {
return new Response("ok");
}
if (url.pathname === "/api/count" && request.method === "POST") {
const current = await env.KV.get("counter") || "0";
const next = parseInt(current) + 1;
await env.KV.put("counter", next.toString());
return Response.json({ count: next });
}
return new Response("Not Found", { status: 404 });
}
};
Step 3: KV namespace
KV is Cloudflare's eventually-consistent key-value store. Read latency under 10ms globally.
wrangler kv namespace create "KV"
# Outputs:
# id = "abc123def456..."
Add to wrangler.toml:
name = "my-api"
main = "src/index.ts"
compatibility_date = "2026-05-01"
[[kv_namespaces]]
binding = "KV"
id = "abc123def456..."
Step 4: D1 database (SQLite at the edge)
wrangler d1 create my-db
# Outputs database_id = "..."
Add to wrangler.toml:
[[d1_databases]]
binding = "DB"
database_name = "my-db"
database_id = "..."
Create a schema migration migrations/0001_init.sql:
CREATE TABLE events (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
occurred_at INTEGER NOT NULL
);
Apply: wrangler d1 migrations apply my-db
Step 5: Local dev
wrangler dev
Wrangler starts a local edge runtime simulator on port 8787. Hot reload on file changes. KV and D1 use local SQLite by default; pass --remote to hit production.
Test: curl http://localhost:8787/health → "ok"
Step 6: Deploy
wrangler deploy
Outputs your worker's URL: https://my-api.<account>.workers.dev. Live globally in under 30 seconds.
Step 7: Custom domain
If you have a domain on Cloudflare DNS, add a route in wrangler.toml:
routes = [
{ pattern = "api.yourdomain.com", custom_domain = true }
]
Deploy again. Cloudflare auto-provisions the cert, points the domain at the Worker.
Step 8: Secrets
Don't put API keys in wrangler.toml. Use:
wrangler secret put OPENAI_API_KEY
# Paste value when prompted
Access in code: env.OPENAI_API_KEY.
Step 9: Observability
Logs: wrangler tail streams real-time logs from production. For persistent logs, enable Workers Logs in the Cloudflare dashboard (free tier: 100k logs/day).
Metrics: dashboard → Workers & Pages → your worker shows requests, CPU time, errors. Set up alerts via Cloudflare Notifications.
Step 10: Limits to know
- CPU time: 50ms on free, 30s on paid. Most APIs fit in free.
- Memory: 128 MB. Don't load large ML models.
- Request size: 100 MB max body.
- Subrequests: 50 outgoing requests per invocation on free, 1000 on paid.
- KV writes: 1 write per second per key, eventually consistent globally.
- D1 storage: 5 GB on free, 10 GB on paid.
Comparison
| Feature | Free tier | Paid (5 USD/month) | AWS Lambda equivalent |
|---|---|---|---|
| Requests/day | 100,000 | 10M included | 1M free, then 0.20 USD/M |
| Cold start | 0ms | 0ms | 100-1000ms |
| CPU time per invocation | 10ms | 30s | 15 min |
| KV reads/day | 100,000 | 10M included | DynamoDB 25 RCU |
| D1 reads/day | 5M | 25M included | RDS Aurora Serverless |
| Custom domain | Yes | Yes | Requires API Gateway |
| Global edge | 300+ cities | 300+ cities | Single region |
FAQ
Can Workers replace AWS Lambda?
For HTTP APIs with low CPU per request, yes. For long-running jobs, ML inference, or anything needing >30s CPU, stick with Lambda or Cloud Run.
How do I handle long-running tasks?
Use Cloudflare Queues (queue the work, process in batches) or Durable Objects (stateful long-lived workers). Or call a separate AWS Lambda from the Worker for the heavy work.
Does D1 support transactions?
Yes, single-statement transactions and batched statements. Multi-statement transactions across the network are not yet supported (June 2026).
What about webhooks?
Workers handle webhooks perfectly. Signature verification, dedup via KV (event ID as key with TTL), queue heavy processing if needed.
Need an edge API built fast?
We've shipped 12 Cloudflare Workers APIs for European customers. 1-week build, fixed price.
Book a discovery call