Reference
TypeScript
Declaration files shipped alongside the JavaScript — no @types/doctreen needed.
DocTreen ships declaration files alongside the JavaScript. No separate
@types/doctreen package is needed.
Express
import express from 'express';
import { expressAdapter, defineRoute, RouteSchemas } from 'doctreen/express';
import { s } from 'doctreen';
const app = express();
app.use(express.json());
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() }),
errors: { 409: 'Email already in use' },
}
));
app.use(expressAdapter(app, { meta: { title: 'My API', version: '1.0.0' } }));
app.listen(3000);Fastify
import Fastify from 'fastify';
import { fastifyAdapter } from 'doctreen/fastify';
const fastify = Fastify();
fastifyAdapter(fastify, { meta: { title: 'My API', version: '1.0.0' } });
fastify.get('/users', async (req, reply) => reply.send([]));
fastify.listen({ port: 3000 });Hono
import { Hono } from 'hono';
import { honoAdapter } from 'doctreen/hono';
const app = new Hono();
honoAdapter(app, { meta: { title: 'My API', version: '1.0.0' } });Koa
import Koa from 'koa';
import Router from '@koa/router';
import { koaAdapter } from 'doctreen/koa';
const app = new Koa();
const router = new Router();
koaAdapter(router, { meta: { title: 'My API', version: '1.0.0' } });
app.use(router.routes());
app.use(router.allowedMethods());NestJS
import 'reflect-metadata';
import { NestFactory } from '@nestjs/core';
import { Controller, Get, Post, Body, Module } from '@nestjs/common';
import { nestAdapter, DocRoute, DocDescription, DocResponse } from 'doctreen/nest';
import { z } from 'zod';
const UserSchema = z.object({ id: z.number(), name: z.string() });
@Controller('users')
class UsersController {
@Get()
@DocDescription('List all users')
@DocResponse(z.array(UserSchema))
getUsers() { return []; }
@Post()
@DocRoute({
description: 'Create a user',
request: { body: z.object({ name: z.string() }) },
response: UserSchema,
errors: { 409: 'Email already in use' },
})
createUser(@Body() body: { name: string }) {
return { id: 1, ...body };
}
}
@Module({ controllers: [UsersController] })
class AppModule {}
async function bootstrap() {
const app = await NestFactory.create(AppModule);
nestAdapter(app, { meta: { title: 'My API', version: '1.0.0' } });
await app.listen(3000);
}
bootstrap();Available types
import type {
SchemaNode, // { type, properties?, items?, optional? }
RouteEntry, // { method, path, params, description, errors, ... }
ErrorEntry, // { status, description, schema }
UserConfig, // full config object shape
NormalizedConfig,
ApiMeta,
RouteRegistry,
} from 'doctreen';
import type { RouteSchemas, ExpressLike } from 'doctreen/express';
import type { RouteSchemas, FastifyLike } from 'doctreen/fastify';
import type { RouteSchemas, HonoLike } from 'doctreen/hono';
import type { RouteSchemas, KoaRouterLike } from 'doctreen/koa';
import type { NestRouteSchemas, NestApplicationLike } from 'doctreen/nest';The structural interfaces (ExpressLike, FastifyLike, etc.) let you use
doctreen without depending on a specific framework's type package.