📖Siyuan's Notes
中文
Tools2026-09-23

Cloudflare Workers Serverless Edge Computing Guide: Deploy Global APIs in 2026

#Cloudflare Workers#Serverless#Edge Computing#API#Performance

Cloudflare Workers Serverless Edge Computing Guide: Deploy Global APIs in 2026

Cloudflare Workers is a serverless platform that runs JavaScript, TypeScript, Rust, Python, and WebAssembly code in 330+ edge locations worldwide. Unlike traditional serverless platforms (AWS Lambda, Google Cloud Functions) that run in a single region, Workers run at the edge — closest to the user. This means a user in Tokyo hits a Worker in Tokyo, and a user in London hits a Worker in London. The result: sub-50ms response times globally, zero cold starts, and automatic scaling to millions of requests. The free plan includes 100,000 requests per day and 10ms CPU time per invocation. This guide covers the complete setup from installation to production deployment with real code examples.

Why Cloudflare Workers in 2026

The serverless edge computing market has grown rapidly. Here is how Workers compares.

Platform Edge Locations Free Plan Cold Starts Languages Max Execution Time
Cloudflare Workers 330+ 100K req/day None JS, TS, Rust, Python, WASM 30s (50ms CPU)
AWS Lambda@Edge 600+ (CloudFront PoPs) No free tier Yes (50-500ms) Node.js, Python 30s
AWS Lambda 30+ regions 1M req/month free Yes (50-500ms) Node.js, Python, Java, Go 15 min
Deno Deploy 35+ regions 1M req/month None TS, JS 5 min (50ms CPU)
Vercel Edge Functions 100+ regions 100K req/month None JS, TS 30s
Fastly Compute@Edge 80+ PoPs No free tier None Rust, JS, WASM 60s (50ms CPU)
Netlify Edge Functions 100+ regions 125K req/month None JS, TS 30s

Workers wins on global reach at zero cost. 330+ edge locations, no cold starts, and 100,000 free requests per day. The main trade-off is the 10ms CPU time limit on the free plan (50ms on paid), which means Workers are best for I/O-heavy work (APIs, routing, auth, caching) not CPU-heavy work (image processing, ML).

Cloudflare Workers Pricing in 2026

Plan Monthly Cost Requests CPU Time Key Features
Free $0 100K/day 10ms KV (100K reads/day), 100K writes/day
Paid (Bundled) $5 10M/month included, $0.30/M after 50ms D1, R2, Queues, Cron Triggers
Paid (Unbound) $0 Pay per use 30s $0.50/M requests, $12.50/M GB-sec
Workers Standard $5 + usage Unlimited 30s Custom limits, priority support

Detailed Pricing for Storage Add-ons

Service Free Tier Paid Pricing Best For
Workers KV 100K reads, 1K writes/day $0.50/M reads, $5/M writes, $0.50/GB storage Key-value cache, config
Workers D1 5GB storage, 5M rows read/day $0.75/M rows read, $19.08/M rows written SQLite at edge
Workers R2 10GB storage, 1M Class A ops/month $0.015/GB/month, $4.50/M Class A ops S3-compatible object storage
Durable Objects 100K requests/day $0.15/M requests + $12.50/M GB-sec Stateful, real-time, WebSocket
Workers Queues 100K operations/day $0.40/M operations Async message queues
Workers Analytics 100K events/day $0.60/M events Custom analytics at edge
Workers Email 200 emails/day $0.40/1K emails Send email from Workers

For a typical API serving 1M requests/month with KV storage and D1 database, your cost would be approximately:

  • Workers requests: $5 (bundled plan, 10M included)
  • KV reads (1M): $0.50
  • D1 reads (5M): $3.75
  • D1 storage (1GB): ~$0.02
  • Total: ~$9.27/month

Compare to AWS Lambda + API Gateway + DynamoDB for the same load: approximately $25-40/month.

Step 1: Installing Wrangler CLI

Wrangler is the CLI tool for building, testing, and deploying Workers.

1.1 Install Node.js and Wrangler

# Install Node.js 18+ (if not installed)
# macOS
brew install node@20

# Ubuntu/Debian
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

# Install Wrangler CLI globally
npm install -g wrangler

# Verify installation
wrangler --version
# wrangler 3.90.0 or later

1.2 Authenticate with Cloudflare

# Login to Cloudflare (opens browser)
wrangler login

# Or use an API token
export CLOUDFLARE_API_TOKEN=your_token_here

# Verify authentication
wrangler whoami

1.3 Create Your First Worker

# Scaffold a new Worker project
npm create cloudflare@latest my-first-worker

# Choose:
# - "Hello World" starter
# - TypeScript
# - Git: Yes

cd my-first-worker
npm install

This creates:

my-first-worker/
├── src/
│   └── index.ts
├── wrangler.toml
├── package.json
├── tsconfig.json
└── .gitignore

1.4 The Worker Code

Open src/index.ts:

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === "/") {
      return new Response(JSON.stringify({
        message: "Hello from Cloudflare Workers!",
        timestamp: Date.now(),
        location: request.cf?.colo || "unknown",
      }), {
        headers: { "Content-Type": "application/json" },
      });
    }

    if (url.pathname === "/health") {
      return new Response("OK", { status: 200 });
    }

    return new Response("Not Found", { status: 404 });
  },
};

The request.cf object contains Cloudflare-specific data: the colo (data center code), country, city, timezone, and more. This lets you serve location-aware responses.

1.5 Test Locally

# Start local dev server
wrangler dev

# Output:
# wrangler dev uses localhost:8787
# Ready on http://localhost:8787

# Test in another terminal
curl http://localhost:8787/
# {"message":"Hello from Cloudflare Workers!","timestamp":...,"location":"LHR"}

curl http://localhost:8787/health
# OK

1.6 Deploy to the Edge

# Deploy globally to 330+ edge locations
wrangler deploy

# Output:
# Uploaded my-first-worker (1.23 sec)
# Published my-first-worker (0.45 sec)
#   https://my-first-worker.username.workers.dev
# Deployed to my-first-worker triggers (0.32 sec)

Your Worker is now live globally. Test it:

curl https://my-first-worker.username.workers.dev/
# {"message":"Hello from Cloudflare Workers!","timestamp":...,"location":"SIN"}

The location field changes based on which edge location serves the request.

Step 2: Workers KV — Key-Value Storage

Workers KV is a globally distributed key-value store. Data written in one location is available in all 330+ locations within seconds.

2.1 Create a KV Namespace

# Create a KV namespace
wrangler kv:namespace create "MY_KV"

# Output:
# { "id": "abc123...", "title": "MY_KV" }

Add the namespace to wrangler.toml:

[[kv_namespaces]]
binding = "MY_KV"
id = "abc123..."

2.2 Read and Write in a Worker

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // Write a value
    if (url.pathname === "/api/set" && request.method === "POST") {
      const body = await request.json();
      await env.MY_KV.put("user:123", JSON.stringify(body));
      return new Response("Saved", { status: 200 });
    }

    // Read a value
    if (url.pathname === "/api/get") {
      const value = await env.MY_KV.get("user:123");
      if (!value) {
        return new Response("Not found", { status: 404 });
      }
      return new Response(value, {
        headers: { "Content-Type": "application/json" },
      });
    }

    return new Response("Not Found", { status: 404 });
  },
};

2.3 KV Performance and Limits

Metric Free Plan Paid Plan
Reads 100,000/day 10M/month included, $0.50/M after
Writes 1,000/day 1M/month included, $5/M after
Storage 1GB $0.50/GB/month
Key size 512 bytes 512 bytes
Value size 25MB 25MB
Read latency ~10ms ~10ms
Write propagation 60 seconds 60 seconds

KV is eventually consistent — writes propagate globally within 60 seconds. For strong consistency, use Durable Objects.

Step 3: Workers D1 — SQLite at the Edge

D1 is Cloudflare's serverless SQLite database that runs at the edge.

3.1 Create a D1 Database

# Create a D1 database
wrangler d1 create my-database

# Output:
# [[d1_databases]]
# binding = "DB"
# database_name = "my-database"
# database_id = "abc123-..."

Add to wrangler.toml:

[[d1_databases]]
binding = "DB"
database_name = "my-database"
database_id = "abc123-..."

3.2 Run Migrations

Create migrations/0001_init.sql:

CREATE TABLE IF NOT EXISTS users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  email TEXT UNIQUE NOT NULL,
  name TEXT NOT NULL,
  created_at TEXT DEFAULT (datetime('now'))
);

CREATE TABLE IF NOT EXISTS posts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id INTEGER NOT NULL,
  title TEXT NOT NULL,
  content TEXT,
  published INTEGER DEFAULT 0,
  created_at TEXT DEFAULT (datetime('now')),
  FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE INDEX idx_posts_user ON posts(user_id);
CREATE INDEX idx_posts_published ON posts(published);

Apply the migration:

wrangler d1 migrations apply my-database --local   # local dev
wrangler d1 migrations apply my-database --remote  # production

3.3 Query D1 in a Worker

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // Create a user
    if (url.pathname === "/api/users" && request.method === "POST") {
      const { email, name } = await request.json();
      const stmt = env.DB.prepare(
        "INSERT INTO users (email, name) VALUES (?, ?) RETURNING *"
      ).bind(email, name);
      const result = await stmt.first();
      return Response.json(result);
    }

    // List published posts
    if (url.pathname === "/api/posts" && request.method === "GET") {
      const { results } = await env.DB.prepare(
        "SELECT * FROM posts WHERE published = 1 ORDER BY created_at DESC LIMIT 20"
      ).all();
      return Response.json(results);
    }

    // Get a single post
    if (url.pathname.startsWith("/api/posts/") && request.method === "GET") {
      const id = url.pathname.split("/")[3];
      const post = await env.DB.prepare(
        "SELECT * FROM posts WHERE id = ?"
      ).bind(id).first();
      if (!post) return new Response("Not found", { status: 404 });
      return Response.json(post);
    }

    return new Response("Not Found", { status: 404 });
  },
};

3.4 D1 vs Other Serverless Databases

Database Free Tier Pricing Latency Best For
Cloudflare D1 5GB, 5M reads/day $0.75/M reads, $19.08/M writes ~5ms edge SQLite workloads at edge
PlanetScale Removed free tier $39/mo (1GB) 50-200ms (region) MySQL, branching
Supabase 500MB, 2 projects $25/mo (8GB) 50-200ms (region) Postgres, real-time
Neon 3GB, 100 compute hr/mo $19/mo (10GB) 50-200ms (region) Serverless Postgres
Turso 9GB, 1B row reads/mo $29/mo (1GB+) ~5ms edge SQLite at edge

D1 wins for SQLite workloads at the edge. For Postgres, consider Supabase or Neon.

Step 4: Workers R2 — S3-Compatible Object Storage

R2 is Cloudflare's object storage that is S3-compatible and has zero egress fees — you pay for storage and operations, not bandwidth.

4.1 Create an R2 Bucket

wrangler r2 bucket create my-images

Add to wrangler.toml:

[[r2_buckets]]
binding = "MY_IMAGES"
bucket_name = "my-images"

4.2 Upload and Serve Files

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    // Upload a file
    if (url.pathname === "/api/upload" && request.method === "POST") {
      const formData = await request.formData();
      const file = formData.get("file") as File;
      const key = `uploads/${Date.now()}-${file.name}`;
      await env.MY_IMAGES.put(key, file.stream(), {
        httpMetadata: { contentType: file.type },
      });
      return Response.json({ url: `https://my-images.username.r2.dev/${key}` });
    }

    // Serve a file
    if (url.pathname.startsWith("/files/")) {
      const key = url.pathname.replace("/files/", "");
      const object = await env.MY_IMAGES.get(key);
      if (!object) return new Response("Not found", { status: 404 });
      return new Response(object.body, {
        headers: { "Content-Type": object.httpMetadata?.contentType || "application/octet-stream" },
      });
    }

    return new Response("Not Found", { status: 404 });
  },
};

4.3 R2 Pricing Comparison

Provider Storage Egress Operations Free Tier
Cloudflare R2 $0.015/GB/mo $0 (free) $4.50/M Class A, $0.36/M Class B 10GB, 1M ops
AWS S3 Standard $0.023/GB/mo $0.09/GB $5/M PUT, $0.40/M GET 5GB (12 months)
Google Cloud Storage $0.020/GB/mo $0.12/GB $5/M Class A, $0.40/M Class B 5GB
Backblaze B2 $0.006/GB/mo Free up to 3x storage $5/M Class A, free Class B 10GB

R2's zero egress is the killer feature. If you serve 1TB of images per month:

  • AWS S3: $23 storage + $90 egress = $113/month
  • Cloudflare R2: $15 storage + $0 egress = $15/month

Step 5: Durable Objects — Stateful Real-Time

Durable Objects provide strongly consistent state at the edge, with support for WebSockets. They are used for chat apps, collaborative editing, real-time games, and rate limiting.

5.1 Create a Durable Object

export class ChatRoom implements DurableObject {
  state: DurableObjectState;
  sessions: Map<WebSocket, string> = new Map();

  constructor(state: DurableObjectState, env: Env) {
    this.state = state;
  }

  async fetch(request: Request): Promise<Response> {
    const pair = new WebSocketPair();
    const [client, server] = Object.values(pair);

    server.accept();
    this.sessions.set(server, "");

    server.addEventListener("message", (event) => {
      for (const [ws] of this.sessions) {
        ws.send(event.data);
      }
    });

    server.addEventListener("close", () => {
      this.sessions.delete(server);
    });

    return new Response(null, { status: 101, webSocket: client });
  }
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    const roomId = url.pathname.split("/")[2] || "default";
    const id = env.CHAT_ROOM.idFromName(roomId);
    const stub = env.CHAT_ROOM.get(id);
    return stub.fetch(request);
  },
};

Add to wrangler.toml:

[[durable_objects.bindings]]
name = "CHAT_ROOM"
class_name = "ChatRoom"

[[migrations]]
tag = "v1"
new_classes = ["ChatRoom"]

Step 6: Custom Domains and Routing

6.1 Add a Custom Domain

  1. Go to Cloudflare Dashboard → Workers & Pages → Your Worker → Triggers → Custom Domains
  2. Click "Add Custom Domain"
  3. Enter your domain: api.yoursite.com
  4. Cloudflare automatically creates DNS records and issues an SSL certificate
  5. Wait 1-5 minutes for DNS to propagate

6.2 Route Multiple Workers on One Domain

Using Workers Routes (requires a domain on Cloudflare DNS):

  1. Go to your domain → Workers Routes → Add route
  2. Route: api.yoursite.com/v1/* → Worker: api-v1-worker
  3. Route: api.yoursite.com/v2/* → Worker: api-v2-worker
  4. Route: yoursite.com/* → Worker: main-site-worker

This lets you run multiple Workers on different paths of the same domain, like a reverse proxy.

Step 7: Cron Triggers

Workers can run on a schedule using Cron Triggers.

Add to wrangler.toml:

[triggers]
crons = ["0 * * * *", "*/15 * * * *"]

This triggers the Worker hourly and every 15 minutes.

export default {
  async scheduled(event: ScheduledEvent, env: Env, ctx: ExecutionContext) {
    // Check for expired tokens in KV
    const keys = await env.MY_KV.list({ prefix: "token:" });
    for (const key of keys.keys) {
      const data = JSON.parse(await env.MY_KV.get(key.name) || "{}");
      if (Date.now() > data.expiresAt) {
        await env.MY_KV.delete(key.name);
      }
    }
  },

  async fetch(request: Request, env: Env): Promise<Response> {
    return new Response("API running");
  },
};

Step 8: Environment Variables and Secrets

8.1 Plain Environment Variables

[vars]
API_URL = "https://api.example.com"
MAX_REQUESTS = "100"

8.2 Secrets

# Set a secret (encrypted, not visible in dashboard)
wrangler secret put STRIPE_SECRET_KEY
# Enter value when prompted

wrangler secret put DATABASE_URL

Access in code:

const stripeKey = env.STRIPE_SECRET_KEY;
const dbUrl = env.DATABASE_URL;

Step 9: Performance Optimization

9.1 Use the Cache API

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const cacheKey = new Request(request.url, request);
    const cache = caches.default;

    // Check cache first
    const cached = await cache.match(cacheKey);
    if (cached) {
      return cached;
    }

    // Generate response
    const response = new Response(JSON.stringify({ time: Date.now() }), {
      headers: {
        "Content-Type": "application/json",
        "Cache-Control": "s-maxage=3600",
      },
    });

    // Store in cache
    ctx.waitUntil(cache.put(cacheKey, response.clone()));
    return response;
  },
};

9.2 Performance Benchmarks

Metric Cloudflare Workers AWS Lambda Vercel Edge
Cold start 0ms 50-500ms 0ms
Global p50 latency 25ms 100-300ms 50-100ms
Global p99 latency 50ms 200-800ms 100-200ms
Throughput (req/sec) Unlimited (auto-scales) 1,000-10,000/concurrent Varies
Deploy time 3-10 seconds 30-60 seconds 10-30 seconds

Step 10: Monetizing Cloudflare Workers Skills

Method Effort Income Potential Time to First $
Build and sell API services High $500-10,000/month 2-6 months
Freelance Worker development Medium $50-150/hour 1-4 weeks
Create and sell Worker templates Medium $200-2,000/month 1-3 months
Tech blog tutorials Medium $200-1,000/article 1-2 weeks
SaaS on Workers Very high $1,000-50,000/month 6-24 months

Build a Micro-SaaS on Workers

A practical side hustle: build a simple API service on Workers and charge for access.

  1. Idea: URL shortener API, image resizer, or IP geolocation API
  2. Stack: Workers + KV/D1 + Stripe for billing
  3. Pricing: Free tier (100 req/day) + Pro ($9/mo, 10K req/day) + Business ($49/mo, 100K req/day)
  4. Time to build: 2-4 weekends
  5. Cost: $5/month (Workers paid plan)
  6. Marketing: Post on Product Hunt, Hacker News, Reddit, Dev.to

Real example: An IP geolocation API on Workers using the request.cf data can be built in a weekend. With 50 paying customers at $9/month, that is $450/month in recurring revenue.

Action Checklist

  • Create a Cloudflare account
  • Install Node.js 20+ and Wrangler CLI
  • Run wrangler login to authenticate
  • Create your first Worker with npm create cloudflare@latest
  • Test locally with wrangler dev
  • Deploy with wrangler deploy
  • Create a KV namespace and use it in your Worker
  • Create a D1 database and run migrations
  • Create an R2 bucket and upload a file
  • Add a custom domain to your Worker
  • Set up environment variables and secrets
  • Create a Cron Trigger for scheduled tasks
  • Use the Cache API for response caching
  • Monitor Workers in the Cloudflare dashboard
  • Set up GitHub Actions for CI/CD
  • Build and deploy a complete API service

Common Pitfalls and Solutions

Pitfall Impact Solution
Exceeding 10ms CPU on free plan Worker errors Upgrade to paid ($5/mo) for 50ms
Treating KV as strongly consistent Stale reads Use Durable Objects for consistency
Not handling errors from D1 API crashes Use try/catch, return proper errors
Large responses over 25MB KV truncation Use R2 for large objects
No rate limiting Abuse, cost overruns Use Cloudflare WAF or Worker logic
No logging Hard to debug Use wrangler tail or Logpush
Testing only locally Edge behavior differs Test on *.workers.dev subdomain

Final Word

Cloudflare Workers is the fastest and cheapest way to run code globally. For $0 (free plan) or $5/month (paid), you get 330+ edge locations, zero cold starts, sub-50ms latency worldwide, and a complete serverless ecosystem: KV for caching, D1 for SQLite at the edge, R2 for zero-egress object storage, Durable Objects for real-time state, and Cron Triggers for scheduled tasks. The setup takes 1-2 hours: install Wrangler, scaffold a Worker, test locally, and deploy. A typical API with 1M requests/month costs under $10 — a fraction of AWS or Vercel. For side hustles and small projects, Workers lets you build a global API, SaaS, or tool without managing a single server. Start with the free plan, build a simple API, add KV and D1 as you need them, and scale up only when traffic demands it.

More guides: bsynet.cc

Tags

#Cloudflare Workers#Serverless#Edge Computing#API#Performance

Related Posts