edit_document// BLOG_POST.md

Cloudflare Workers: Build and Deploy Edge APIs in Under 10 Minutes

//

, ,

Traditional serverless platforms run your code in a single region. Your users in Tokyo hit a Lambda in us-east-1, adding 150-200ms of latency before your code even executes. Cloudflare Workers flip this model: your code runs on every node in Cloudflare’s network, over 300 locations across 100+ countries. The request is handled by the nearest edge node. Cold starts are under 5ms because Workers use V8 isolates, not containers. For latency-sensitive APIs, this changes everything.

How Workers Differ from Lambda

AWS Lambda spins up a container per function instance. Cold starts range from 100ms (Node.js) to 2+ seconds (Java). Workers use V8 isolates, the same sandboxing technology behind Chrome tabs. An isolate spins up in under 5ms with a fraction of the memory footprint. The trade-off: Workers have a 128MB memory limit and no access to the Node.js standard library (no fs, no child_process). You get the Web Platform APIs: Fetch, Streams, Crypto, URL, TextEncoder, and the Cache API.

Your First Worker

# Install and scaffold
npm create cloudflare@latest my-api
cd my-api

The scaffolded src/index.ts is your entry point:

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

    // Simple routing
    if (url.pathname === '/api/health') {
      return Response.json({ status: 'ok', region: request.cf?.colo });
    }

    if (url.pathname === '/api/time') {
      return Response.json({
        utc: new Date().toISOString(),
        edge: request.cf?.colo,       // Which Cloudflare data center handled this
        country: request.cf?.country,  // Geo of the requester
      });
    }

    if (url.pathname === '/api/echo' && request.method === 'POST') {
      const body = await request.json();
      return Response.json({
        received: body,
        headers: Object.fromEntries(request.headers),
      });
    }

    return new Response('Not Found', { status: 404 });
  },
};
# Run locally with hot reload
npx wrangler dev

# Deploy to production (all 300+ edge locations)
npx wrangler deploy

That is it. Your API is live globally in under 30 seconds. No region selection, no load balancer configuration, no container registry.

Adding Persistent Storage with KV

Workers KV is a globally distributed key-value store. Eventually consistent (reads propagate in under 60 seconds), optimized for read-heavy workloads. Create a namespace and bind it in wrangler.toml:

# wrangler.toml
name = "my-api"
main = "src/index.ts"
compatibility_date = "2025-01-01"

[[kv_namespaces]]
binding = "CACHE"
id = "abc123"  # From: npx wrangler kv namespace create CACHE
// Using KV in your Worker
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);

    if (url.pathname === '/api/config') {
      // Read from KV (global, cached at edge)
      const config = await env.CACHE.get('app-config', 'json');
      return Response.json(config ?? { version: '1.0.0' });
    }

    if (url.pathname === '/api/config' && request.method === 'PUT') {
      const body = await request.json();
      // Write to KV with 1-hour TTL
      await env.CACHE.put('app-config', JSON.stringify(body), {
        expirationTtl: 3600,
      });
      return Response.json({ updated: true });
    }

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

Durable Objects for Strong Consistency

When you need strong consistency (counters, rate limiters, collaborative state, WebSocket rooms), Durable Objects provide a single-threaded, transactional compute primitive. Each Durable Object has a unique ID and runs on exactly one edge node, ensuring sequential access without race conditions:

// Rate limiter as a Durable Object
export class RateLimiter {
  state: DurableObjectState;

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

  async fetch(request: Request): Promise<Response> {
    const key = new URL(request.url).searchParams.get('key') ?? 'default';
    const now = Date.now();
    const window = 60_000; // 1 minute
    const limit = 100;

    // Transactional storage -- no race conditions
    let requests: number[] = (await this.state.storage.get(key)) ?? [];
    requests = requests.filter((t) => t > now - window);

    if (requests.length >= limit) {
      return Response.json({ allowed: false, remaining: 0 }, { status: 429 });
    }

    requests.push(now);
    await this.state.storage.put(key, requests);
    return Response.json({ allowed: true, remaining: limit - requests.length });
  }
}

When to Use Workers

Workers excel at API gateways, authentication/authorization at the edge, A/B testing, request transformation, lightweight APIs, and anything where global latency matters. They are not suited for long-running compute (30-second CPU limit on free, 15 minutes on paid), heavy file processing, or workloads requiring the full Node.js API surface. Pair Workers with traditional serverless or containers for compute-heavy backends while handling the latency-sensitive edge layer in Workers.

Further reading: Cloudflare Workers Docs | Workers KV Docs | Durable Objects Docs


arrow_circle_right// POST_NAVIGATION

forum// COMMENTS

Leave a Reply

Your email address will not be published. Required fields are marked *