Express / Fastify
Familiar and fast to ship. Typed clients mean OpenAPI, codegen, or manual duplication. The contract lives outside the code that actually runs.
Stop maintaining OpenAPI files, generators, and hand-written clients. Chain routes on Deno, export the type, call them from anywhere. Fully typed.
Most stacks treat types as an afterthought. You ship routes, then a second contract, then a generated client, and hope they stay in sync.
Familiar and fast to ship. Typed clients mean OpenAPI, codegen, or manual duplication. The contract lives outside the code that actually runs.
Excellent Web Standards story. End-to-end types usually mean RPC helpers or parallel schemas, still a second layer to maintain.
Great types, different paradigm. Procedure-based RPC, tighter coupling, and a jump away from plain HTTP routing many teams already know.
Write HTTP routes once. Keep Express-shaped DX. Get a client that already knows every URL, param, body, and response, without generating a single file.
Type safety is the headline. Day to day you also get a familiar API, composable routers, and batteries that usually mean extra packages.
get/post/use, params, middleware. Your team already knows the shape. Deno and Web Standards underneath.
Export typeof server. HttpClient checks URLs, methods, bodies, and responses at compile time. No OpenAPI sync job.
Middleware declares what it adds to req.data. Nested routers declare what they need. Wrong order fails in the editor.
Built-in CORS, schema validation, modular routers, custom 404/errors, and server.fetch for portless tests.
Not a kitchen sink. The surface that makes Express-shaped APIs feel complete on Deno.
Mount Router trees with prefixes. Parent context requirements are checked when you mount. Paths normalize so matching stays consistent.
.use() or per-route. Each step can add context or short-circuit. Registration order matches at runtime and in the type system.
Built-in z schemas on body, query, and params. Invalid requests return 400 before your handler. Types update with the schema.
No runtime link to the server module. Separate packages work. Wrong routes and bodies fail before you hit the network.
Defaults that work, per-router rules, OPTIONS preflight. Override with .cors() when you need credentials or dynamic origins.
Call your app as a standard fetch handler. No port, no process. Pair with Deno.test for fast route checks.
ExpressAPI makes the router type the contract. Inference does the rest at compile time. Nothing extra ships at runtime.
Every chained call widens the server type. Export typeof server and that is your API surface.
HttpClient only needs an import type. No server starts. No runtime dependency on your route module.
Request, Response, fetch, Deno. Express-inspired syntax without inventing a parallel HTTP universe.
Pick the tool that matches the job. ExpressAPI is for Deno APIs that want Express-shaped routing and a typed client without codegen.
| Express | Hono | Fastify | tRPC | ExpressAPI | |
|---|---|---|---|---|---|
| Primary runtime | Node | Multi | Node | Node / TS | Deno |
| Feel | Routes + MW | Ultra-light WS | Perf + plugins | RPC procedures | Express-like |
| Typed HTTP client | Manual / OpenAPI | RPC / schemas | Manual / schema | Yes | Yes, no codegen |
| Contract source | Often external | Schemas / RPC | JSON Schema | Procedures | Router type |
| Typed middleware context | No | Partial | Decorators / types | Context | TAdds / TNeeds |
Stay on Hono if multi-runtime is the priority. Choose ExpressAPI when you want Deno + Express muscle memory + a typed client with zero generation step.
tRPC wins for procedure RPC. ExpressAPI wins when you want real HTTP routes, status codes, CORS, and a client that still feels like REST, fully typed.
Until it drifts. Here the running router is the source of truth. No second file to keep honest.
Yes, by design. Built on Deno and Web Standards. If you need Node-first, Express or Fastify remain the better fit.
Add the package, chain a few routes, export the type. If the editor catches a bad client call before you run anything, that is the point.
deno add jsr:@webtools/expressapi