| name | cloudflare-workers-author |
| description | Workflow for building production-grade Cloudflare Workers in TypeScript - bootstrap, bindings and storage choice, routing (Hono + RPC via WorkerEntrypoint), testing with @cloudflare/vitest-pool-workers, gradual deployments, and common pitfalls. Use when adding a new Worker, adding endpoints to an existing Worker, choosing between KV / R2 / D1 / Durable Objects / Hyperdrive, setting up testing, configuring deployment, or reviewing a Worker PR. |
Cloudflare Workers Author
Companion rule: 401-cloudflare-workers.mdc (file-scoped to wrangler.{jsonc,toml,json} and common Worker entry-point names). The rule is the gate (non-negotiables); this skill is the workflow (how to actually build the Worker).
Voice: opinionated. Cloudflare Workers is a small, fast platform with a specific runtime model. Most production incidents come from treating it like a generic Node.js server (top-level await, mutable module state, unbounded loops, secrets in logs, public-URL Worker-to-Worker calls). The workflow below prevents those.
When to invoke this skill
Invoke when:
- The user asks to build a new Cloudflare Worker, Durable Object, Workflow, Queue consumer, Cron Trigger, Email Worker, or Pages Function.
- The user is adding routes / endpoints to an existing Worker.
- The user asks "should I use KV / D1 / R2 / DO / Hyperdrive for this?" (the bindings-and-storage decision matrix is the answer).
- The user is setting up testing for a Worker.
- The user is configuring deployment (gradual rollouts, OIDC for GitHub Actions, secret rotation).
- A PR review touches
wrangler.jsonc, a Worker entry-point file, or a binding configuration.
Do NOT invoke for:
- Cloudflare Pages static-site work (not Worker authoring). See
cloud-platforms/references/cloudflare.md § Pages.
- WAF rule authoring (use
cloudflare-waf-author skill instead).
- Generic JavaScript / TypeScript code that happens to be imported by a Worker - delegate to the
typescript-javascript skill.
Pre-step: classify what you're building
Each Worker type has a different entry-point shape and different bindings emphasis. Ask the user which one if unclear - the wrong entry-point shape is a wrong-handler-signature bug that won't surface until deploy time.
| Type | Entry point | Most common bindings |
|---|
| Default-export HTTP Worker | export default { async fetch(req, env, ctx) {...} } satisfies ExportedHandler<Env> | KV, D1, R2, AI, Service Bindings |
| Named-entrypoint (RPC target) | export default class extends WorkerEntrypoint { async myMethod() {...} } | Same, called via Service Binding from another Worker |
| Durable Object | export class Room extends DurableObject {...} + a default fetch handler that routes to DO | DO storage + KV / R2 for cold data |
| Workflow | export class MyWorkflow extends WorkflowEntrypoint { async run(event, step) {...} } | KV, D1, R2, Service Bindings, AI |
| Queue consumer | export default { async queue(batch, env, ctx) {...} } | D1, R2, Service Bindings, AI |
| Cron Trigger | export default { async scheduled(event, env, ctx) {...} } | KV, D1, R2, Service Bindings |
| Email Worker | export default { async email(message, env, ctx) {...} } | R2 for attachments, D1 for logs |
| Pages Function | export const onRequest: PagesFunction<Env> = async (ctx) => {...} (in functions/[[path]].ts) | KV, D1, R2 (subset of full Worker bindings) |
The six-step workflow
Step 1 - Bootstrap the project
If this is a new Worker (no existing wrangler.jsonc), follow references/project-bootstrap.md to get a minimal Module Workers project with:
wrangler.jsonc (the modern config format - JSONC with comments; recommended by Cloudflare since Wrangler v3.91.0)
package.json with wrangler, typescript, and a types script that runs wrangler types
tsconfig.json with strict mode, ESNext target, "types": [], and the generated worker-configuration.d.ts in include
src/worker.ts with the generated Env type (do not hand-author it) and satisfies ExportedHandler<Env>
.dev.vars in .gitignore; worker-configuration.d.ts generated by wrangler types
[!IMPORTANT]
wrangler types now generates the Workers runtime types and supersedes @cloudflare/workers-types. Current Wrangler (4.9x+) prints this on every wrangler types run and tells you to uninstall @cloudflare/workers-types and remove it from tsconfig.json. The generated worker-configuration.d.ts provides both the global Env (from vars + declared secrets) and the runtime globals (Request, Response, ExecutionContext, ExportedHandler, etc.), pinned to your compatibility_date. So: no @cloudflare/workers-types dependency, "types": [] in tsconfig.json, and worker-configuration.d.ts in include. Declare expected secrets under secrets.required in wrangler.jsonc so they appear in the generated Env. Vars are typed as their literal values by default; run wrangler types --strict-vars=false to type them as string when a var's value varies by environment.
If the user already has a Worker, read their existing wrangler.jsonc before suggesting changes. Don't propose a fresh structure that conflicts with their conventions.
Step 2 - Choose bindings and storage
This is the most consequential design decision in any new Worker. Pick wrong and you'll be migrating data in six months. The full decision matrix is in references/bindings-and-storage.md; the headline:
| Need | Use | Don't use |
|---|
| Session / config / feature flag (small, eventually consistent, low write) | KV | D1, R2 |
| Relational queries, transactions, joins (single-region, SQLite scale) | D1 | KV |
| File / blob storage (any size; metadata + body) | R2 | KV (25 MB value limit) |
| Strongly-consistent coordination (sessions, rate limits, websocket fan-out) | Durable Object | KV (eventually consistent), D1 (single-region, not strongly consistent across Workers) |
| Connection pooling to existing Postgres / MySQL | Hyperdrive | Direct TCP (works but no pooling) |
| Job queues, dead-letter, batching | Queues | Manual KV-based queue |
| LLM inference | Workers AI behind AI Gateway (cost cap + caching + observability) | Direct provider API calls |
| Vector search | Vectorize | KV with embedding lookups (no ANN index) |
Document the choice in the PR description with the rejected alternatives and why.
Step 3 - Route requests
Pick a router based on complexity:
| Complexity | Choice | Why |
|---|
| 1-3 routes, no middleware | Native URL + switch | Zero deps, smallest bundle |
| Many routes, middleware, typed env | Hono (de facto) | Typed, fast, designed for edge runtimes, OpenAPI integration available |
| Worker-to-Worker calls only | RPC via WorkerEntrypoint | No HTTP overhead, typed arguments and return values, faster than HTTP service bindings |
| GraphQL | GraphQL Yoga + Hono | Yoga has a Workers adapter |
For Hono patterns (typed env via Hono<{ Bindings: Env, Variables: ... }>, middleware, error boundaries, OpenAPI) and RPC patterns (when to use, named entrypoints, lifecycle), see references/hono-patterns.md.
Step 4 - Test
The canonical testing path is @cloudflare/vitest-pool-workers (current stable v0.16.x; requires Vitest 4.1+). It runs tests inside the Workers runtime via Miniflare, with bindings mocked or real (your choice), HMR for fast reruns, and isolated per-test storage.
See references/testing-with-vitest-pool.md for setup details. The pattern:
vitest.config.ts registers the cloudflareTest() Vite plugin (exported from @cloudflare/vitest-pool-workers) inside a standard defineConfig from vitest/config, pointing at your wrangler.jsonc
tsconfig.json for tests includes "@cloudflare/vitest-pool-workers/types" in types (declares the cloudflare:test module)
- Unit tests: import the handler, call
worker.fetch(request, env, ctx) directly with mocked env
- Integration tests: use
SELF.fetch() from cloudflare:test (in-process) or auxiliary Workers (fresh isolate)
[!IMPORTANT]
The defineWorkersConfig helper and the @cloudflare/vitest-pool-workers/config subpath were removed in v0.16. Older guidance (import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config") fails on 0.16+ with Missing "./config" specifier. The current API is the cloudflareTest() Vite plugin from the package root: cloudflareTest({ wrangler: { configPath: "./wrangler.jsonc" } }) added to plugins in a vitest/config defineConfig. The cloudflare:test module types now come from the @cloudflare/vitest-pool-workers/types subpath (not the bare package name).
Reject in review: new tests using unstable_dev (deprecated; the vitest-pool-workers path replaces it).
Step 5 - Deploy and roll out gradually
Workers supports two deployment models:
- Standard deploy (
wrangler deploy) - upload and immediately serve 100% from the new version. Fine for low-risk changes.
- Gradual deployment (
wrangler versions upload then wrangler versions deploy) - upload as a new version without serving traffic, split traffic between old and new, then promote to 100%. Required for production HTTP Workers serving customer traffic.
Headline commands:
wrangler versions upload
wrangler versions deploy
wrangler versions deploy <old-version-id>@90 <new-version-id>@10
wrangler versions deploy <new-version-id>@100
Smoke-test a specific version in production via the Cloudflare-Workers-Version-Overrides request header (the version override applies even when its percentage is 0%).
For CI integration (OIDC trust for GitHub Actions instead of long-lived API tokens), secret rotation, and the canary-then-promote pattern, see references/deployment-and-versions.md.
Step 6 - Verify and ship
Before merge:
wrangler types regenerated; worker-configuration.d.ts matches wrangler.jsonc
tsc --noEmit clean
wrangler deploy --dry-run clean (catches binding misconfigurations)
- Vitest pool-workers tests pass
- PR description names the rejected storage alternatives and why
- PR description names the soak plan for the gradual rollout (e.g., "10% for 1h, then 50% for 1h, then 100%")
- Reviewer checklist from
401-cloudflare-workers.mdc ticked
Self-check before submitting
The high-leverage subset of the 401-cloudflare-workers.mdc reviewer checklist:
See references/common-pitfalls.md for the full list of failure modes observed across many Workers teams, organized by symptom (cold-start regressions, intermittent state pollution, secret leaks, subrequest budget exhaustion, deployment drift).
Related
401-cloudflare-workers.mdc - the rule (file-scoped non-negotiables)
400-cloudflare.mdc - broader Cloudflare platform playbook
405-cloudflare-waf-rules.mdc - file-scoped WAF rule playbook
cloud-platforms skill - cross-cloud platform patterns
cloudflare-waf-author skill - WAF rule authoring workflow companion
skills/typescript-javascript - general TypeScript / JavaScript patterns