Skip to content

Why Routa

Every REST codebase eventually needs an answer to one question: what does this route accept, what can it return, and what context is available when it runs? Routa exists because most TypeScript backends answer that question in three places at once, and those places drift apart.

What does this route accept, what can it return, and what context is available when it runs?

A team building a TypeScript REST API generally picks one of these.

Hand-written routes plus hand-written OpenAPI

Section titled “Hand-written routes plus hand-written OpenAPI”

The fastest thing to start. The contract lives in a YAML file that nothing enforces, so the document describes the API you meant to build rather than the one you deployed.

Metadata sits next to the handler, which keeps them together. The tradeoff is a runtime reflection layer, a dependency-injection container, and a stack trace that goes through framework internals before it reaches your code.

Generate types from OpenAPI, write handlers by hand

Section titled “Generate types from OpenAPI, write handlers by hand”

The document stays authoritative and types follow it. The tradeoff is that the source no longer tells you what a route does; you read generated .d.ts files, and any change begins in YAML rather than in the editor where you are already working.

Write the contract in TypeScript and derive everything from it

Section titled “Write the contract in TypeScript and derive everything from it”

Types, validation, and OpenAPI all come from one declaration. The tradeoff is that the framework has to be opinionated about the shape of a route, and you accept its shape.

Routa takes the last option. The route contract in source is the single source of truth, and everything else is derived from it:

schemas -> routes -> handlers -> OpenAPI -> docs/checks

For teams that start from an agreed API contract, Routa runs the first step in reverse once, then hands the source back to you:

openapi.yaml/json -> schemas -> route files -> handler stubs

Routa is opinionated at the HTTP boundary: routing, validation, context, middleware, responses, and OpenAPI. It does not own your database, services, models, policies, or application architecture.

  • You write contracts before handlers. A route declares its inputs and its full set of named responses. Returning something undeclared is a type error, not a runtime surprise.
  • Generated files are committed. .routa/ holds route metadata, the OpenAPI baseline, and a regeneration manifest. They belong in review, like a lockfile.
  • Zod and Hono are not swappable in v0. Routa v0 targets one schema library and one runtime so the contract model can be validated end to end before it is generalized.
  • Business logic stays yours. Routa gives handlers typed input and typed context, then gets out of the way. Services, persistence, and domain rules live in your modules.

Routa is at a v0 implementation baseline. Additional runtime adapters, non-Zod schema support, SDK generation, and a plugin system are deferred rather than rejected. See v0 Scope & Status for the current line between the two.