Overview

A Worker is a fetch handler that Cloudflare runs at its edge. This guide scaffolds one, binds a KV namespace, tests locally, deploys, and verifies. Limits, pricing, and design rules are in cloudflare-workers; KV semantics are in cloudflare-kv.

Prerequisites

  • A Cloudflare account. The Free plan allows 100,000 requests per day and 10 ms CPU time per request; paid plans lift the daily cap.
  • Node 22 or newer and npm. Install Wrangler in the project, not globally: npm i -D wrangler@latest (the scaffold does this).

Steps

1. Scaffold

npm create cloudflare@latest -- my-worker
cd my-worker
npx wrangler login

Pick the “Hello World” Worker. The scaffold creates wrangler.jsonc (the default config format), src/index.ts, and tsconfig.json.

2. Write the handler

export interface Env { MY_KV: KVNamespace }
 
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    if (url.pathname === "/health") return new Response("ok");
    const key = url.searchParams.get("key");
    if (!key) return new Response("Missing key", { status: 400 });
    const value = await env.MY_KV.get(key);
    return value === null ? new Response("Not found", { status: 404 }) : Response.json({ key, value });
  },
};

3. Bind KV

npx wrangler kv namespace create MY_KV      # prints the namespace id
{
  "name": "my-worker",
  "main": "src/index.ts",
  "compatibility_date": "2026-10-01",
  "kv_namespaces": [{ "binding": "MY_KV", "id": "<id from the command>" }]
}

The binding name must equal the property in Env. Keep compatibility_date recent; it pins runtime behavior.

4. Test locally

npx wrangler dev                     # http://localhost:8787
npx wrangler kv key put --binding=MY_KV hello world
curl "http://localhost:8787/?key=hello"

wrangler dev uses a local simulation of KV by default, so it never touches production data. Without --remote on the put, the key goes to the local store; add --remote to write to the real namespace.

5. Deploy

npx wrangler deploy

For a custom domain, add "routes": [{ "pattern": "api.example.com", "custom_domain": true }]; the zone must be on Cloudflare. For CI, run npx wrangler deploy with CLOUDFLARE_API_TOKEN stored as a repository secret (github-actions).

Verify it worked

npx wrangler kv key put --binding=MY_KV ping pong --remote
curl -s https://my-worker.<your-subdomain>.workers.dev/health        # ok
curl -s "https://my-worker.<your-subdomain>.workers.dev/?key=ping"   # {"key":"ping","value":"pong"}
npx wrangler tail

Common errors

  • No account id found. Set account_id in wrangler.jsonc or CLOUDFLARE_ACCOUNT_ID in the environment.
  • The binding is undefined. The binding name differs from the Env property.
  • A key written during wrangler dev is missing in production. It went to the local store; write with --remote.
  • A route does not match. A Custom Domain cannot be created on a hostname that already has a CNAME record.

FAQ

How many requests does the Cloudflare Workers Free plan allow?

The Free plan allows 100,000 requests per day and 10 ms CPU time per request; paid plans lift the daily cap.

Does wrangler dev use my production KV data?

No; wrangler dev uses a local simulation of KV by default. Add --remote to write to the real namespace.

Why is my KV binding undefined in a Worker?

The binding name in wrangler.jsonc differs from the property in Env; they must match exactly.

Should I install Wrangler globally?

No; install it in the project with npm i -D wrangler@latest, which the create-cloudflare scaffold does for you.