The answer. A Worker is a JavaScript (or TypeScript, Python, WASM) module that exports handlers. fetch(request, env, ctx) runs for every HTTP request; scheduled runs on a cron; queue consumes a Queue; email receives mail. It runs on Cloudflare's runtime, workerd, inside a V8 isolate, not a container: the code is loaded during the TLS handshake, so there is no cold start worth measuring, and the memory ceiling is 128 MB per isolate. Everything the Worker talks to on Cloudflare (KV, R2, D1, Queues, Workers AI, another Worker) is declared in wrangler.jsonc and shows up as a property of env. Wrangler is the CLI for all of it: create, run locally, deploy, tail logs, manage storage.
The pattern.
// src/index.ts — the smallest complete Worker
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(request.url);
if (url.pathname === "/health") return Response.json({ ok: true });
const cached = await env.CACHE.get(url.pathname); // a KV binding
return new Response(cached ?? "hello", { headers: { "content-type": "text/plain" } });
},
} satisfies ExportedHandler<Env>;
// wrangler.jsonc — the binding that makes env.CACHE exist
{ "name": "hello", "main": "src/index.ts", "compatibility_date": "2026-09-01",
"kv_namespaces": [{ "binding": "CACHE", "id": "<namespace-id>" }] }
Watch out.
- It is not Node.
nodejs_compatpolyfills a lot, but check a package's runtime support before you build on it. - Bindings are the only way in. There are no connection strings in code; the same source deploys to staging and production with different
envvalues. - CPU time is what is metered, not wall time. Waiting on
fetch()is free; hashing a 200 MB file is not.
Related: first-worker-create-dev-deploy-in-five-commands · vars-secrets-bindings-all-arrive-on-env · workers-limits-cpu-time-not-wall-time