Why ExpressAPI

The motivation behind a Deno framework where the router type is the contract.

The friction

API teams keep rediscovering the same tax: you write routes, then you write a second description of those routes so clients stay typed. OpenAPI, shared Zod packages, codegen pipelines - every change becomes two changes. Drift is not a bug; it is the default.

TypeScript can do better when the route definitions themselves carry the types. Deno made sharing those types across packages straightforward with import type.

The bet

What if the server type was the contract? Chain routes on HttpServer, export typeof server, pass it to HttpClient. Params inferred from URLs. Middleware context checked for order. Schemas that validate and narrow. Zero runtime cost for types.

Principles

  • Web Standards first - Request, Response, fetch, Headers. No parallel HTTP universe.
  • Types without codegen - the router type is the source of truth.
  • Small surface - HttpServer, Router, HttpClient, Middleware, z. Helpers when you need them.
  • Honest composition - middlewares declare adds/needs; sub-routers declare parent context; the compiler catches lies early.
  • Library, not platform - server.fetch for tests, mount routers, deploy wherever Deno runs.

Non-goals

  • Being the best choice on Node (use Express / Fastify).
  • Replacing procedure RPC (use tRPC when that model fits).
  • Multi-runtime adapters as a primary focus (Hono owns that space).