Schema builder
Lightweight `s` builder when you don't want a runtime schema library.
DocTreen ships a lightweight schema builder for defining typed request and
response shapes without a runtime schema library. It exists in all three
languages with the same vocabulary — s in Node and Python, S:: in PHP:
const { s } = require('doctreen');
// import { s } from 'doctreen';
s.string() // { type: 'string' }
s.number() // { type: 'number' }
s.boolean() // { type: 'boolean' }
s.null() // { type: 'null' }
s.unknown() // { type: 'unknown' }
s.object({
id: s.number(),
name: s.string(),
bio: s.optional(s.string()), // optional field — shown with ? in the UI
})
s.array(s.string())
s.array(s.object({ id: s.number(), tag: s.string() }))
s.optional(s.string()) // wraps any node as optional
s.enum(['admin', 'user'])
s.default(s.enum(['admin', 'user']), 'user')
s.nullable(s.string())s is re-exported from all adapter packages for convenience:
const { s } = require('doctreen/express');
const { s } = require('doctreen/fastify');
const { s } = require('doctreen/hono');
const { s } = require('doctreen/koa');
const { s } = require('doctreen/nest');from doctreen import s
s.string()
s.number()
s.boolean()
s.null()
s.unknown()
s.object({
"id": s.number(),
"name": s.string(),
"role": s.default(s.enum(["admin", "user"]), "user"),
"deletedAt": s.nullable(s.optional(s.string())),
})
s.array(s.string())
s.optional(s.string())The builder mirrors the Node s helper method-for-method — the
conformance suite
keeps the OpenAPI output byte-identical.
use Doctreen\Schema\S;
S::string();
S::number();
S::boolean();
S::null();
S::unknown();
S::object([
'id' => S::number(),
'name' => S::string(),
'bio' => S::optional(S::string()),
]);
S::array(S::string());
S::optional(S::string());Builder vs a validating schema library
s / S:: builder | Zod (Node) | Pydantic (Python) | |
|---|---|---|---|
| Used for docs / OpenAPI | Yes | Yes | Yes |
| Used for runtime validation | No (descriptive only) | Yes | Yes |
| Dependency | None | zod | pydantic |
| Refinements / custom validators | No | Yes | Yes |
Mixed routes work fine — only validating schemas (Zod on Node, Pydantic on
Python) validate when validate: true; builder-declared routes pass through.
A builder node describes a shape; checking against a description would either
reject valid payloads or wave invalid ones through.