A Cloudflare Worker is serverless code that answers requests on Cloudflare’s global network.
The simplest mental model:
request -> Worker -> response
You write a handler, almost always fetch(). When a request hits your domain or a route tied to the Worker, Cloudflare runs that code and expects a Response.
You’re not managing a VM. You’re not standing up Express on a VPS. You’re not babysitting a Node process that dies at 3 a.m.
You’re shipping a piece of code the platform can run for you.
What “edge” means (and what it doesn’t)
In this context:
network locations closer to the user than a traditional central backend.
If your backend only lives in us-east, a user in Colombia, Mexico, or Chile can pay round-trip latency on every important request. With Workers, some of your HTTP logic can run on Cloudflare’s global network.
That does not mean everything is magically faster or better designed. It means you have another layer for entry logic.
Don’t say:
“Your backend starts at the edge.”
Say:
“The first layer of your API can run on Cloudflare’s network, usually closer to the user than your central backend.”
What problem it solves (and what it doesn’t)
Many apps start like this, and that’s fine:
user -> CDN -> central backend -> database -> response
You don’t need to throw that model away.
But some requests don’t need to travel all the way to the main backend:
- language or country redirects
- small webhooks
/api/health- light auth middleware
- header normalization
- proxying to another service
- basic validation before delegating
Workers are good for putting that logic near the entrance.
Don’t think “I replace my whole backend.”
Think:
I have a programmable HTTP layer before, during, or around my backend.
The minimal Worker
export default {
async fetch(request, env, ctx): Promise<Response> {
return Response.json({
ok: true,
message: "Hello from Workers",
path: new URL(request.url).pathname,
});
},
} satisfies ExportedHandler<Env>;
Three pieces:
request— what arrived from the userenv— bindings and configctx— execution context (ctx.waitUntil(), etc.)
For day one, this is enough:
fetch takes a Request and returns a Response.
Where it sits in the architecture
client -> Worker -> resource or service
That resource can be another backend, R2, D1, KV, a Queue, a Durable Object, AI Gateway, or another Worker. When you wire those in, you get bindings.
That’s why Workers aren’t “just a function.” They’re the front door into the platform.
Example: one concrete route
export default {
async fetch(request, env, ctx): Promise<Response> {
const url = new URL(request.url);
if (url.pathname === "/api/health") {
return Response.json({
status: "ok",
service: "pricing-api",
});
}
return new Response("Not found", { status: 404 });
},
} satisfies ExportedHandler<Env>;
This isn’t trying to be a full app. That’s the point: Workers shine on concrete HTTP slices.
When I would use Workers
I’d use Workers for:
- small endpoints
- webhooks
- smart redirects
- middleware
- APIs for frontends
- proxy or routing between services
- fast validation
- producing messages to a Queue
- starting a Workflow
- calling AI Gateway with extra control
When I wouldn’t put everything there
I’d be careful if:
- the work runs a long time
- you need multi-step retries
- you want strong state per user, room, or document
- the work is heavy (images, batch jobs)
- you’re dumping too many responsibilities into one handler
Inside the same ecosystem:
| You need | Look at |
|---|---|
| Per-entity state | Durable Objects |
| Async work | Queues |
| Durable multi-step processes | Workflows |
| Object storage | R2 |
| SQL | D1 |
| Simple key-value | KV |
Common failure: the giant Worker
The first Worker starts clean. Then people bolt on auth, email, images, AI, analytics, CRM, and session coordination. Suddenly you have a function you can’t debug.
Better mental model:
Worker = HTTP entry
Queue = async work
Durable Object = per-entity state
Workflow = long multi-step process
R2 / D1 / KV = storage by use case
Before production
- Which routes does it own?
- What must respond immediately?
- What can go to a Queue?
- What shows up in
env? - What logs do you need?
- How do you split dev / staging / prod?
- What happens when a dependency fails?
A small Worker can be simple. A system of Workers needs design.
The line to remember
Request -> fetch() -> Response
Workers are the door. Architecture starts when you decide what that door should do—and what it should hand off.
Cloudflare series for builders: Workers · Bindings · Durable Objects · Queues
Docs: Workers · How Workers works · Runtime APIs · Bindings