DocTreen

Zod integration

First-class Zod support on every adapter.

Zod is a first-class input format on every adapter. Pass any Zod schema where DocTreen expects a schema — defineRoute, @DocRoute, individual @Doc* decorators — and it is converted to DocTreen's internal SchemaNode representation at definition time. No zodToSchemaNode() wrapper required.

const { defineRoute } = require('doctreen/express');
const { z } = require('zod');

const CreateUser = z.object({ name: z.string(), email: z.string().email() });

app.post('/users', defineRoute(
  (req, res) => res.status(201).json({ id: 1, ...req.body }),
  {
    description: 'Create a user',
    request:  { body: CreateUser },
    response: CreateUser.extend({ id: z.number() }),
    errors:   { 409: 'Email already in use' },
  }
));

The same shape works in doctreen/fastify, doctreen/hono, doctreen/koa, and via @DocRoute({ ... }) in doctreen/nest.

zodToSchemaNode (standalone)

If you ever need the converter directly (e.g. to build a shared schema bag outside of an adapter), it remains available as a standalone export:

import { z } from 'zod';
import { zodToSchemaNode } from 'doctreen/zod';

const schema = z.object({
  id:    z.number(),
  name:  z.string(),
  email: z.string().email(),
  role:  z.enum(['admin', 'user']).optional(),
  tags:  z.array(z.string()),
});

const node = zodToSchemaNode(schema);
// {
//   type: 'object',
//   properties: {
//     id:    { type: 'number' },
//     name:  { type: 'string' },
//     email: { type: 'string' },
//     role:  { type: 'string', optional: true },
//     tags:  { type: 'array', items: { type: 'string' } },
//   }
// }

Supported Zod types

Zod typeSchemaNode output
z.string(), z.date(){ type: 'string' }
z.number(), z.bigint(){ type: 'number' }
z.boolean(){ type: 'boolean' }
z.null(), z.undefined(), z.void(){ type: 'null' }
z.any(), z.unknown(){ type: 'unknown' }
z.object({...}){ type: 'object', properties: {...} }
z.array(T){ type: 'array', items: T }
z.tuple([T, ...]){ type: 'array', items: T[0] }
z.record(V){ type: 'object', properties: {} }
z.optional(T){ ...T, optional: true }
z.nullable(T){ ...T, optional: true }
z.default(T)unwraps to T
z.enum([...]){ type: 'string' }
z.nativeEnum(E){ type: 'string' } or { type: 'number' }
z.literal(v){ type: typeof v }
z.union([A, B])first option
z.discriminatedUnion(...)first option
z.intersection(A, B)merged object properties
z.lazy(...)resolved recursively
.transform(), .pipe()unwraps to input schema

Zod schemas double as runtime validators via the same declaration — see Runtime validation.

On this page