itty-router

An ultra-tiny (~1kB), zero-dependency TypeScript router built for Cloudflare Workers and other serverless/edge runtimes.

Framework
npm
v5.0.24
2,047stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
47/100Fair
Development Activity16
Maintenance20
Community52
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
75/100Good
Architecture78
Code Quality80
Innovation85
Learning Curve55

itty-router is a microrouter designed around a single constraint: size. Where routers like Express weigh in at hundreds of kilobytes, itty-router’s most minimal build (IttyRouter) starts at roughly 450 bytes, with the batteries-included AutoRouter variant landing around 970 bytes — small enough that bundle size stops being a tax on every request in a Cloudflare Worker, Deno Deploy function, or other cold-start-sensitive environment.

Under the hood it uses a Proxy to turn any HTTP verb into a chainable route-registration method (router.get(...), router.post(...), arbitrary custom methods included), compiling each route pattern into a named-capture regex at registration time. Dispatch is a single fetch(request) call that walks the compiled routes, matches the pathname, and threads params and parsed query-string values onto the request object before invoking handlers in order.

AutoRouter builds on the lower-level Router primitive to add a full middleware lifecycle — before hooks, a catch handler for centralized error formatting, and finally hooks for response post-processing (including automatic JSON formatting and a 404 fallback) — giving Express-like ergonomics without Express-like weight. The package also ships companion utilities for CORS handling, cookie/content parsing middleware, typed response helpers (json, text, html, image formats), and a StatusError class for throwing HTTP errors directly from route handlers.

Because it operates entirely on the standard Fetch API Request/Response objects, it works unmodified across Cloudflare Workers, Deno, Bun, Node (18+), and the browser — anywhere the Fetch API is available.

What You Get

  • IttyRouter — the smallest possible router: route registration via Proxy-based verb methods and regex-based path matching, with no built-in middleware lifecycle.
  • Router — adds before, catch, and finally hook arrays around route dispatch for a full middleware/error-handling lifecycle.
  • AutoRouter — a batteries-included Router preconfigured with automatic JSON response formatting, a default 404 handler, and centralized error formatting out of the box.
  • Route and query parsing — named params (:id), greedy params (:path+), and wildcards (*) compiled to regex at registration time; query strings parsed into a request.query object automatically.
  • Response helpersjson, text, html, jpeg, png, webp, and status formatters for returning typed Response objects with one call.
  • CORS utilities — a cors() factory producing preflight and corsify functions to handle OPTIONS requests and append CORS headers to any response.
  • Error handling — a StatusError class and error() formatter that convert thrown errors (or explicit status codes) into properly-shaped JSON error responses.
  • Middleware add-onswithParams, withCookies, and withContent for binding proxy-based request enhancements without extra dependencies.

Common Use Cases

  • Building lightweight REST APIs on Cloudflare Workers where every extra kilobyte adds to cold-start time and Worker size limits.
  • Serverless functions (Deno Deploy, Bun, edge middleware) that need routing without pulling in a full Node.js-oriented framework.
  • Prototyping APIs quickly with AutoRouter’s zero-config JSON formatting, 404 handling, and error catching.
  • APIs that need CORS support without a separate middleware package — using the built-in cors() helper alongside route handlers.
  • Projects that want Express-like route/method chaining (router.get().post()) but need to stay under strict bundle-size budgets.

Under The Hood

Architecture The core of both IttyRouter and Router is a Proxy wrapped around an empty object: its get trap intercepts any property access (router.get, router.post, router.anything) and returns a function that compiles the given path into a named-capture regex — via a compact chain of .replace() calls handling greedy params (:name+), named params (:name), literal dots, and wildcards (*) — then pushes a [method, regex, handlers, path] tuple onto a shared routes array. Dispatch is a single fetch(request, ...args) method that parses the query string into request.query, then walks routes matching method and pathname against each compiled regex in registration order, invoking handlers until one returns a non-null response. Router layers a full middleware lifecycle on top of this — before hooks run first (can short-circuit), route handlers run inside a try/catch that defers to a user-supplied catch handler, and finally hooks post-process the response (this is where AutoRouter injects automatic JSON formatting and a default 404 responder). The design means changing the core route-matching regex logic in one small block would ripple through every router variant and every published sub-package, since they all consume the same routes array shape.

Tech Stack Written entirely in TypeScript with zero runtime dependencies, compiled to a hybrid ESM/CJS distribution (itty-packager, the maintainer’s own build/lint/release CLI, drives bun run build/lint/release). Tests run under Bun’s built-in test runner (bun test --coverage), with coverage reported to Coveralls via GitHub Actions across separate test, lint, and tag-triggered release workflows. The package has no framework, ORM, or database dependency by design — it operates purely on standard Fetch API Request/Response objects, which is what lets it run unmodified on Cloudflare Workers, Deno, Bun, Node 18+, and browsers.

Code Quality The src/ directory pairs almost every implementation file with a co-located *.spec.ts test file (11 spec files against roughly a dozen source modules), and the project advertises 100% coverage tracked continuously via Coveralls, enforced on every push and pull request through GitHub Actions. Error handling is explicit and typed: a dedicated StatusError class carries an HTTP status plus arbitrary metadata, and a single error() formatter normalizes thrown errors, StatusError instances, or raw status codes into consistent JSON error bodies. Naming is consistent (PascalCase router factories, camelCase helpers and middleware), and linting runs through a shared config pulled from the maintainer’s itty-packager package. The codebase does lean on @ts-ignore/@ts-expect-error in a handful of spots to work around the dynamic Proxy-based API surface — a deliberate tradeoff of type-checker friction for bundle size and API ergonomics.

What Makes It Unique Most routers (Express, Koa, even most “micro” alternatives) build their route tables as arrays or tries walked by conventional loops with substantial supporting infrastructure. itty-router instead compiles routes into named-capture regexes at registration time and uses a single Proxy get trap to make every HTTP verb a chainable method with no per-verb code — collapsing what is normally hundreds of lines of dispatch logic into a few dozen. Combined with the before/catch/finally middleware model in Router, this gives close to Express-level ergonomics while keeping the smallest configuration under a kilobyte, which is a genuinely distinctive tradeoff for cold-start-sensitive serverless and edge environments rather than a generic feature checklist.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search