Skip to content

Route Contracts

Routa is built around one source of truth:

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

Route files define what each HTTP method accepts, what it can return, which middleware applies, and where the handler boundary begins.

Folders map to URL segments:

src/routes/users/route.ts
src/routes/users/$userId/route.ts
src/routes/users/$userId/posts/route.ts

Each route.ts file owns the methods for one path. Keeping all verbs for one path together makes shared params, middleware, schemas, and metadata easier to maintain.

export default defineRoute({
post: createRoute({
input: {
body: CreateUserSchema,
},
responses: {
success: {
status: 201,
schema: UserSchema,
},
emailConflict: {
status: 409,
schema: EmailConflictSchema,
},
},
run: async ({ input, ctx }) => {
return users.createUser({
body: input.body,
actorId: ctx.state.auth.principal.id,
});
},
}),
});

Handlers return named outcomes from the declared response map. If a route did not declare an outcome, TypeScript should reject it.

Routa owns routing, validation, middleware context, typed responses, and OpenAPI. Your application owns services, use cases, domain models, persistence, and business logic.