MervCodes

Tech Reviews From A Programmer

Cloudflare Workers Guide: Serverless at the Edge

1 min read

Cloudflare Workers Guide: Serverless at the Edge

Traditional serverless platforms like AWS Lambda run your code in a handful of regional data centers. Cloudflare Workers takes a fundamentally different approach: your code runs in over 300 cities worldwide, milliseconds away from nearly every user on the planet. This is serverless at the edge, and it changes the latency math for a huge class of applications.

In this guide we'll cover how Workers actually work, when to reach for them, and the practical patterns that separate a toy script from a production-grade edge application.

What Makes Workers Different

Most serverless functions are built on containers or micro-VMs. When a request arrives and no warm instance exists, the platform has to spin one up — the dreaded "cold start," which can add hundreds of milliseconds or more.

Cloudflare Workers instead use the V8 isolate model, the same engine that powers Chrome. Instead of booting an entire container per function, the runtime spins up a lightweight JavaScript isolate. Isolates start in under 5 milliseconds and consume a tiny fraction of the memory a container needs. The practical result: effectively zero cold starts, and the ability to pack thousands of tenants onto a single machine cheaply.

The trade-off is that you're not running arbitrary Linux binaries. You're running JavaScript, TypeScript, or anything that compiles to WebAssembly (Rust, Go, C, and more). For the vast majority of API and web workloads, that's plenty.

Your First Worker

A Worker is just a module that exports a fetch handler. Here's a minimal example:

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    if (url.pathname === "/health") {
      return new Response("ok", { status: 200 });
    }
    return new Response("Hello from the edge!", {
      headers: { "content-type": "text/plain" },
    });
  },
};

The three arguments are worth understanding:

  • request — a standard Request object, the same Fetch API you'd use in a browser.
  • env — your bindings: environment variables, secrets, KV namespaces, D1 databases, R2 buckets, and so on.
  • ctx — execution context, most importantly ctx.waitUntil() for running work after the response is sent.

To develop and deploy, use Wrangler, the official CLI:

npm install -g wrangler
wrangler login
wrangler init my-worker
wrangler dev      # local development with hot reload
wrangler deploy   # push to the global network

Your wrangler.toml file holds configuration — the entry point, compatibility date, routes, and bindings.

The Storage Ecosystem

A Worker on its own is stateless. The power comes from pairing it with Cloudflare's edge storage primitives. Choosing the right one is the single most important architectural decision you'll make.

KV — Eventually Consistent Key-Value

Workers KV is optimized for high-read, low-write workloads. Reads are served from cache at the edge and are extremely fast, but writes take time to propagate globally (eventual consistency, typically under a minute). Use it for configuration, feature flags, cached API responses, and session lookups where slight staleness is acceptable.

D1 — SQLite at the Edge

D1 is a serverless SQL database built on SQLite. It's ideal when you need relational queries, transactions, and strong consistency within a region. It's perfect for small-to-medium applications that have outgrown key-value storage.

const { results } = await env.DB.prepare(
  "SELECT * FROM users WHERE id = ?"
).bind(userId).all();

R2 — Object Storage Without Egress Fees

R2 is S3-compatible object storage with zero egress fees, which makes it dramatically cheaper than AWS S3 for anything with high download volume — media, backups, large datasets.

Durable Objects — Strongly Consistent Coordination

When you need a single source of truth — a chat room, a collaborative document, a rate limiter, a game lobby — Durable Objects give you a single-threaded, strongly consistent compute+storage primitive addressed by ID. They're the answer to "but how do I coordinate state across the edge?"

Practical Patterns

Run Work After the Response

Don't make the user wait for logging, analytics, or cache warming. Use waitUntil:

async fetch(request, env, ctx) {
  const response = await handle(request, env);
  ctx.waitUntil(logRequest(request, env)); // fires without blocking
  return response;
}

Cache Aggressively with the Cache API

Workers have direct access to Cloudflare's CDN cache. For expensive computations or upstream calls, cache the result:

const cache = caches.default;
let response = await cache.match(request);
if (!response) {
  response = await buildExpensiveResponse(request);
  ctx.waitUntil(cache.put(request, response.clone()));
}
return response;

Mind the Limits

The free tier allows 100,000 requests per day. CPU time per request is bounded (10ms on the free plan, configurable up to 5 minutes of wall-clock for paid plans on long-running tasks). The key discipline: Workers are for I/O-bound glue logic, not heavy CPU crunching. Offload compute-intensive work to WebAssembly or a backend service.

Keep Secrets Out of Code

Never hardcode API keys. Use Wrangler secrets, which are encrypted and injected via env:

wrangler secret put STRIPE_API_KEY

When to Use Workers — And When Not To

Reach for Workers when you need: low-latency APIs, request routing and rewriting, A/B testing, authentication at the edge, geolocation-based personalization, webhook handling, or a full-stack app (paired with Workers Pages and the storage primitives above).

Look elsewhere when you need: long-running compute jobs, large in-memory datasets, native binary dependencies that won't compile to Wasm, or sub-millisecond control over a specific data center's hardware. In those cases a regional container platform or a traditional VM is a better fit.

A Realistic Architecture

A common production setup looks like this: a Worker sits at the front door handling auth, rate limiting (via a Durable Object), and routing. Static assets come from Pages and R2. Dynamic relational data lives in D1, with hot configuration cached in KV. Background tasks are queued through Cloudflare Queues and processed by consumer Workers. Observability flows through console.log into the dashboard's live tail (wrangler tail) and into your analytics pipeline via waitUntil.

This gives you a globally distributed application with no servers to patch, no regions to choose, and a cost model that scales to zero.

FAQ

Q: Do Workers really have no cold starts? For practical purposes, no. Isolates start in single-digit milliseconds, so you won't see the multi-hundred-millisecond cold starts typical of container-based serverless. The first request to a brand-new deployment may pay a tiny compilation cost, but it's negligible.

Q: Can I run Node.js code on Workers? Increasingly, yes. Cloudflare provides Node.js compatibility mode (nodejs_compat) that polyfills many built-in modules like crypto, buffer, and stream. Pure-JS npm packages generally work; anything requiring native binaries or full filesystem access will not.

Q: How much does it cost? The free tier covers 100,000 requests per day. The paid plan starts at $5/month and includes 10 million requests, with low per-million pricing beyond that. Storage products (KV, D1, R2) are billed separately, and R2's zero egress fees often make it the standout value.

Q: How do I handle a database that needs strong consistency? Use D1 for relational, transactional data, or Durable Objects when you need a single coordination point for state that multiple requests mutate concurrently. Avoid KV for anything requiring read-after-write consistency.

Q: Can Workers call my existing backend? Absolutely. A very common pattern is using Workers as a smart proxy in front of an origin server — adding caching, auth, and request transformation at the edge while your existing infrastructure handles the heavy lifting.

Q: How do I debug a Worker in production? Run wrangler tail to stream live logs from production, use the dashboard's real-time logs view, and add structured logging via console.log. For deeper tracing, integrate an observability provider through the tail Workers feature.

Conclusion

Cloudflare Workers move your code to where your users are, eliminating cold starts and slashing latency by running on a truly global network. Start small — a single fetch handler and wrangler dev — then layer in KV, D1, R2, and Durable Objects as your needs grow. The mental shift is the hardest part: stop thinking in regions and servers, and start thinking in requests handled at the edge.

Sources

Related Articles