index.ts and it would work. But as your Worker grows, a feature-router layout scales cleanly and mirrors Express conventions that most teams already know. Each feature gets its own Router, its own file, and its own scoped middleware. The entry point wires them together.
Directory layout
Entry point pattern
Yoursrc/index.ts is the composition root. It creates the app, applies global middleware in order, mounts each feature router at its prefix, registers the global error handler, and exports the fetch entrypoint.
src/index.ts
Middleware registration order matters.
requestId() runs before logger() so the logger can include req.id in its output. Always register your global error handler last — after all routes and sub-routers are mounted.Feature routers
Each file undersrc/routes/ exports a single Router instance. The router handles its own scoped middleware, declares its own routes, and optionally defines a local error boundary for domain-specific errors.
src/routes/users.ts
src/index.ts:
src/index.ts (excerpt)
Shared types
Two files insrc/types/ keep the TypeScript story clean across your entire codebase.
- types/env.ts
- types/blaze.d.ts
env.ts is the single source of truth for your Cloudflare bindings. It must stay in sync with wrangler.toml. Every binding name and type maps one-to-one.src/types/env.ts
Wrangler configuration
wrangler.toml is the authoritative declaration of your Worker’s bindings, routes, and compatibility settings. Keep your src/types/env.ts in sync with every binding declared here.
wrangler.toml
wrangler.toml, add the matching property to Env in src/types/env.ts immediately. TypeScript will then surface every handler that needs updating — no grep required.