Skip to content

Routa

Routa is a schema-first, OpenAPI-aware REST framework for new TypeScript APIs. Choose your entry point based on whether you want a generated starter, need to add Routa to an existing TypeScript API, or already have an OpenAPI contract.

Use the scaffolder when you want the fastest path to a working Routa application:

Terminal window
npm create routa-ts@latest

You receive a status route, tests, configuration, and committed .routa/ metadata. The tradeoff is adopting Routa’s generated project shape before you have chosen each file.

Follow Installation when you already have a TypeScript codebase and want to migrate one HTTP boundary at a time. You keep control of the surrounding application, but you must add the scripts, configuration entry point, and generated metadata yourself.

Use Scaffold from OpenAPI when a reviewed OpenAPI document is the starting contract. Routa generates the first source representation and a baseline; after that import, route files become the source of truth and regeneration must be previewed.

Routes are filesystem-backed. Folders map to URL segments, and each route.ts file owns every method for that path.

src/routes/users/route.ts
import { createRoute, createRouteRoot } from "@routa-ts/core";
import { CreateUserSchema, UserSchema } from "./schemas.js";
import * as users from "../../services/users.js";
const route = createRouteRoot("/users");
export default route({
post: createRoute({
input: {
body: CreateUserSchema,
},
responses: {
success: {
status: 201,
schema: UserSchema,
},
},
run: async ({ input }) => {
return { type: "success", data: await users.createUser(input.body) };
},
}),
});

input describes what the request must contain. Routa parses params, query, headers, cookies, and body through those Zod schemas before the handler runs, so input.body is already validated and typed.

responses is a map of named outcomes to a status and a schema. The handler returns one of those names with its data. Returning an undeclared name is a type error, and returning data that does not match the schema is caught at runtime before it reaches the client.

Everything else is derived. The CLI reads these files to generate route metadata, produce OpenAPI, and check that the contract has not drifted from its committed baseline.

  • One route contract drives TypeScript types, runtime validation, generated metadata, and OpenAPI, so contract changes appear in several reviewable outputs.
  • Filesystem paths and createRouteRoot(path) must agree; moving a route is an API change.
  • Business logic does not belong in run. Routa owns routing, validation, middleware context, typed responses, and OpenAPI, while services, domain models, and persistence stay application-owned.
  • .routa/ is committed source-derived state, not a disposable build artifact.