Engineering

Cloudflare Workers Tutorial: Deploy a Serverless API in 15 Minutes

May 2026 · 11 min read

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

Related Posts

Webhook Security API Rate Limiting
← All blog posts

0ms cold start, 100k requests/day free

Cloudflare Workers for production APIs. We've shipped 12.

Book a discovery call