DocTreen

defineRoute

Explicit route definition with full schema control.

For full control, wrap a handler with defineRoute. Works the same across Express, Fastify, Hono, and Koa:

const { defineRoute, s } = require('doctreen/express');
// const { defineRoute, s } = require('doctreen/fastify');
// import { defineRoute, s } from 'doctreen/hono';
// const { defineRoute, s } = require('doctreen/koa');

app.post('/users', defineRoute(
  (req, res) => {
    res.status(201).json({ id: 1, name: req.body.name });
  },
  {
    description: 'Create a new user account',
    headers: {
      Authorization:  'Bearer <token>',
      'Content-Type': 'application/json',
    },
    request: {
      body:  s.object({ name: s.string(), email: s.string(), role: s.optional(s.string()) }),
      query: null,
    },
    response: s.object({ id: s.number(), name: s.string(), email: s.string() }),
    errors: {
      409: 'Email address already in use',
      422: { description: 'Validation failed', schema: s.object({ message: s.string(), field: s.string() }) },
    },
  }
));

Schema resolution order

AdapterPriority
ExpressdefineRoute → JSDoc → runtime inference
FastifydefineRoute → Fastify native JSON Schema → JSDoc
HonodefineRoute → JSDoc
KoadefineRoute → JSDoc
NestJS@DocRoute / @Doc* decorators

Options

OptionTypeNotes
descriptionstringHuman-readable description shown in the UI
summarystringShort title for the operation
headersRecord<string, string>Request headers — example value as the map value
request.bodyZod | SchemaNodeRequest body
request.queryZod | SchemaNodeQuery parameters
request.paramsZod | SchemaNodePath parameters (v1.15+)
responseZod | SchemaNode | Record<number, Zod | SchemaNode>Success response — a single schema (200) or a status-keyed map (v1.15+)
errorsRecord<number, string | { description?, schema? }>Documented error responses
validatebooleanPer-route override of adapter-level validate setting
tagsstring[]Override the path-segment default tag
securityArray<Record<string, string[]>>Per-route security override
hiddenbooleanHide from docs UI and OpenAPI
examplesRouteExamplesMulti-example bodies and responses (v1.11+)
callbacksRecord<string, CallbackDef>Per-operation OpenAPI 3.1 callbacks

Path parameters v1.15

request.params declares a schema for path parameters, alongside body and query. It's validated when validate is on (a structured 422 on mismatch) and typed as the path parameters in the OpenAPI export:

defineRoute(handler, {
  request: {
    params: z.object({ id: z.string().uuid() }),
    query:  z.object({ page: z.coerce.number().default(1) }),
    body:   z.object({ name: z.string() }),
  },
});

Status-keyed responses v1.15

response accepts either a single schema (documented as 200) or a status-keyed map, letting each status carry its own schema in the OpenAPI export. The single-schema form is unchanged:

defineRoute(handler, {
  response: {
    201: z.object({ id: z.number() }),
    202: z.object({ queued: z.boolean() }),
  },
});

With TypeScript generics

import { defineRoute, RouteSchemas } from 'doctreen/express';
import { s } from 'doctreen';

app.post('/users', defineRoute<{ name: string }, never, { id: number; name: string }>(
  (req, res) => res.status(201).json({ id: 1, name: req.body.name }),
  {
    description: 'Create a user',
    request:  { body: s.object({ name: s.string() }) },
    response: s.object({ id: s.number(), name: s.string() }),
  }
));

See TypeScript for the full type catalogue.

On this page