AV
HomeAboutProjectBlog

© 2026 Ave syah Shina. All rights reserved.

  1. Home
  2. Blog
  3. 02 — Request Handling, Fetch, and the Workers Lifecycle

02 — Request Handling, Fetch, and the Workers Lifecycle

August 13, 20269 min read
Download as Markdown

"It runs your code on a request" described what a Worker does without explaining its shape. The model that straightened it out: a Worker is fundamentally a single fetch handler that receives a Request and returns a Response, and everything else — bindings, caching, middleware, service bindings — is plumbing attached to that one exchange. [1] Once I saw the request/response cycle as the spine, every other feature snapped into place around it.

The framing that landed is that a Worker is a function with a specific signature. The whole platform is organized around that signature, and the lifecycle is short: the request arrives, the handler runs, the response leaves, the isolate is done [2]. State that needs to survive past that one exchange lives in bindings, not in the Worker's own memory.

Request method, url, headers auth logging fetch handler (request, env, ctx) KV D1 R2 service bindings (env) fetch(origin) optional Response status, body, headers isolate terminates once the Response is returned

The fetch handler: the one entry point

A Worker exports one thing that matters: a fetch handler. Everything else is in service of it. The signature is fixed:

export default {
async fetch(request, env, ctx) {
return new Response("Hello");
},
};

Three parameters, each load-bearing [1]:

  • request — a standard web Request object. Method, URL, headers, body — all here. The same Request type the browser's Fetch API uses.
  • env — the bindings: KV namespaces, D1 databases, R2 buckets, secrets, environment variables, service bindings to other Workers. This is how the Worker touches anything outside itself.
  • ctx — the execution context. Its killer feature is ctx.waitUntil(promise), which lets work continue briefly after the response is sent (for logging, analytics, cache writes).

The contract is: take a Request, return a Response. That's it. Everything in the Workers ecosystem is an elaboration of that one function.

The Fetch API and runtime APIs

Inside the handler, the Fetch API is the primary tool for reaching outward — calling an origin server, a third-party API, another service [3]. It's the browser's fetch, adapted for the server side:

const upstream = await fetch("https://api.example.com/data", {
headers: { Authorization: `Bearer ${env.API_KEY}` },
});

The runtime also exposes a set of Cloudflare-specific APIs on top of the web standards — caches for the Cache API, the KV binding methods, HTMLRewriter for streaming HTML modification. These aren't globals in the Node sense; they're either always-available (caches, crypto) or attached to a binding (KV, D1) [3]. The mental shift from Node is: nothing is imported from a package, everything is handed to you via env or available as a web standard.

The lifecycle: short, stateless, disposable

The lifecycle is the part I had to internalize before statelessness stopped feeling like a limitation [2]:

  1. A request hits the edge location nearest the user.
  2. Cloudflare routes it to a Worker isolate (spawning one if none is warm).
  3. The fetch handler runs, consulting bindings and upstream APIs as needed.
  4. The Response is returned to the user.
  5. The isolate is done — terminated, or kept warm briefly for the next request on the same edge.

There is no persistent state between requests unless I explicitly put it in a binding. Global variables set in one invocation are visible to subsequent invocations on the same warm isolate, but that's an optimization, not a contract — I can never rely on a global surviving. State I need to keep goes in KV (eventually consistent, global), D1 (SQL), or Durable Objects (strongly consistent, single-actor) [2].

Bindings: the only way out

Bindings are the configuration that connects a Worker to anything external, and they're declared in wrangler.toml rather than in code [4]. The categories:

  • KV namespaces — read-heavy key-value storage.
  • Durable Objects — strongly consistent stateful objects.
  • R2 buckets — object storage.
  • D1 databases — SQL.
  • Queues — message queues (producer and consumer).
  • Service bindings — direct calls to other Workers, no network hop.
  • Secrets — encrypted environment variables (API keys).
  • Environment variables — non-secret config.

The reason bindings matter as a concept is that they replace what I'd otherwise do with connection strings and API keys scattered through code. A KV namespace isn't a URL my Worker connects to — it's an object my Worker is _given_, in env. The binding is the relationship; the resource is behind it [4].

Service bindings: Worker-to-Worker, no network

Service bindings deserve a callout because they're the cleanest composition primitive [5]. A service binding lets one Worker call another as if it were a function — the caller invokes env.OTHER_WORKER.fetch(request), and the call stays inside Cloudflare's network, never touching the public internet. No API key, no DNS, no TLS handshake overhead, no egress.

This is what makes microservice-style architectures viable on Workers. Splitting a monolithic Worker into several smaller ones — one per bounded domain — doesn't cost a network round trip per call, the way it would if they communicated over HTTP. The binding is a direct in-network channel [5].

Caching strategies

Workers give fine-grained cache control via the Cache API — I can read from and write to Cloudflare's cache programmatically, not just via HTTP headers [6]. The three patterns worth memorizing:

  • Cache-first — check cache; if hit, return it; if miss, fetch from origin, cache, return. Best for static content.
  • Network-first — always fetch from origin, then refresh the cache. Best for content that must be fresh.
  • Stale-while-revalidate — return cached immediately if present, then fetch the fresh version in the background for next time. Best for content where slightly-stale is acceptable but you want eventual freshness.

The choice is a freshness-versus-latency trade. Stale-while-revalidate is the default I reach for — it gives instant responses from cache while quietly fixing the staleness in the background.

Middleware patterns

Middleware is just composition — chaining handlers so each does one job (auth, logging, header injection) before passing the request to the next [7]. Workers don't ship a middleware system in the box, but frameworks like Hono and patterns like the one in the reference make it a solved problem. The shape is always the same: a function takes the request and a next function, does its thing, and either short-circuits with a response or calls next() to continue the chain.

This is where code organization pays off. A Worker that grew into a tangle of inline auth and logging logic is a candidate for middleware extraction — the request flows through a pipeline, each stage has one responsibility [7].

Logging and monitoring

The bare essentials: console.log in development, wrangler tail in production for real-time logs streaming from the edge [8]. For anything beyond poking around, the metrics that matter are request count, CPU time per request, error rate, and cache hit ratio — all surfaced in the dashboard. The habit I built: log structured JSON, not strings, so that when I ship logs to an external service they're queryable rather than grep-fodder.

How I use this

The request/response cycle is the spine of every Worker I write, so I keep the handler thin: middleware for cross-cutting concerns, the handler for the actual logic, bindings for any state, and ctx.waitUntil for anything that doesn't need to block the response. When a Worker starts feeling complex, the question I ask is whether I'm trying to put too much in one handler — at which point service bindings and a split into multiple Workers usually clarify things.

References

[1] Cloudflare, "Request and Response," Cloudflare Workers Runtime APIs, 2024. [Online]. Available: https://developers.cloudflare.com/workers/runtime-apis/request

[2] Cloudflare, "How Workers works," Cloudflare Docs, 2024. [Online]. Available: https://developers.cloudflare.com/workers/reference/how-workers-works/

[3] Cloudflare, "Fetch API in Workers," Cloudflare Workers Runtime APIs, 2024. [Online]. Available: https://developers.cloudflare.com/workers/runtime-apis/fetch

[4] Cloudflare, "Bindings (env)," Cloudflare Workers Runtime APIs, 2024. [Online]. Available: https://developers.cloudflare.com/workers/runtime-apis/bindings/

[5] Cloudflare, "Service Bindings," Cloudflare Workers Platform, 2024. [Online]. Available: https://developers.cloudflare.com/workers/platform/service-bindings/

[6] Cloudflare, "How the cache works · Cloudflare Workers," Cloudflare Docs, 2024. [Online]. Available: https://developers.cloudflare.com/workers/reference/how-the-cache-works/

[7] Cloudflare, "Middleware · Cloudflare Pages Functions," Cloudflare Docs, 2024. [Online]. Available: https://developers.cloudflare.com/pages/functions/middleware/

[8] Cloudflare, "Debugging and logging · Cloudflare Pages," Cloudflare Docs, 2024. [Online]. Available: https://developers.cloudflare.com/pages/functions/debugging-and-logging/

Knowledge check · Question 1 of 5

What is the required entry point for a Cloudflare Worker?

Comments

Leave a Comment

You must be signed in to comment

0 Comments

No comments yet. Be the first to comment!