Releases: CanDgrmc/doctreen
Release list
v1.15.0 — Validation, completed
Validation, completed — this release closes the gap between what a route
documents and what it enforces, and fixes the codegen type-safety holes
that pushed consumers back to hand-written types.
Added
- Path-parameter schemas + validation —
defineRoutenow accepts
request: { params }, so:idand friends are validated by the same Zod
schema that documents them, with a structured 422 on mismatch. Works across
Express, Fastify, Hono, Koa, and NestJS — no more hand-rolledrequireUuid
helpers repeated on every route. - Request write-back. Opt in with
validate: { writeback: true }and the
parsed payload — Zod coercions and.default()s applied — is written back
ontoreq.body/req.query/req.params, so the handler reads coerced
values instead of re-parsing them. (Koa and Hono expose coerced path params
viactx.state.doctreenValidated/c.get('doctreenValidated').) - Dev-mode response assertion.
validate: { response: 'warn' | 'throw' }
checks the handler's response against the declared Zodresponseschema —
'warn'logs a mismatch and passes through,'throw'surfaces a 500 in
development. Never coerces the response. - Status-keyed responses.
response: { 201: schema, 200: schema }documents
each status with its own schema and drives the OpenAPI export; the plain
response: schemaform still means200. defaultErrorsconfig. Declare shared error responses (401 / 403 / 422 …)
once — they merge into every route, with the route's ownerrorswinning on a
status conflict.- Standard
DoctreenValidationErrorenvelope. Validated routes document
their 422{ error, issues[] }body as a named component, so codegen emits a
DoctreenValidationErrortype and clients stop guessing the error shape. - Offline OpenAPI emit.
getOpenApiDocument(app)(on every adapter) and the
newdoctreen emit-openapiCLI build a staticopenapi.jsonwithout starting
a server, so codegen and CI can run fully offline.
Fixed
defineSchemanow works with Zod schemas. Named schemas were matched by
object identity, which Zod→SchemaNode conversion breaks — so Zod-defined named
schemas fell through to anonymousSchema1/Schema2types in codegen. They
now emit$ref: '#/components/schemas/<name>'and codegen produces
interface User, at the top level, nested, and inside arrays.- Deep schemas no longer collapse to
unknown. The Zod conversion depth cap
was raised from 5 to 12, so deep-but-non-recursive trees (menu → categories →
items → variants → options) keep their leaf types. Recursion is best expressed
withdefineSchema, which now emits a terminating$ref.
v1.14.1 — codegen: valid TS for nullable objects
Fixed
codegenno longer emits invalid TypeScript for nullable objects. A
nullable object schema — OpenAPI 3.1type: ["object", "null"]or 3.0
nullable: true— renders as{ … } | null, which starts with{but is
not a bare object body. The generator mistook it for an interface body and
producedexport interface Profile { … } | null(a syntax error). Such
schemas now emit a validtypealias (export type Profile = { … } | null);
plain objects still emitexport interface.
v1.14.0 — schema enum/nullable/default builders
Added
-
s.enum,s.nullable,s.default, ands.literalschema builders. The
shelper can now express value-level facets that previously had nowhere to
live in aSchemaNode:defineRoute(handler, { request: { body: s.object({ role: s.enum(['admin', 'user', 'guest']), status: s.nullable(s.enum(['active', 'inactive'])), limit: s.default(s.number(), 20), kind: s.literal('user'), }), }, });
These flow through to the exported OpenAPI document, the docs UI schema tree,
and every request example (Copy as cURL, Postman export, mock server).
Fixed
-
defineRoutenow emitsenumandnullable.SchemaNodecould only
carrytype/properties/items/optional, so enum members and
nullability were dropped before reaching the OpenAPI export or docs UI —
z.enum([...])collapsed to a barestringand.nullable()was demoted to
optional. Both thesbuilder and the Zod converter now preserve them, and
the exporter emitsenumplus OpenAPI 3.1type: [<type>, 'null']. -
Default values are now applied in request examples. A schema
default
(vias.default(...)or Zod.default(...)) is emitted to OpenAPI and seeds
the generated request bodies and query parameters used by Copy as cURL, the
Postman export, and the mock server. Fields with a default are treated as
optional and excluded fromrequired.
v1.13.1 — codegen: all methods per path + correct enum/null
Fixed
-
codegennow emits every HTTP method on a shared path. Operations that
reused anoperationIdacross HTTP verbs — or used a verb-agnostic
operationId— collapsed to a single generated name, so the typed client
silently kept only the last method on a path (e.g.POST /usersclobbered
GET /users).
Generated type and function names are now guaranteed unique; colliding names
are disambiguated by HTTP method (usersGet/usersPost). -
codegenenum / nullable output is now correct. Anullpresent both in
a 3.1type: ["string", "null"]array (or vianullable: true) and in an
enumproduced a doubled union such as"active" | "inactive" | null | null.
Nullability is now folded into a single trailing| nulland enum members are
deduplicated. Type coverage was extended along the way toconstliterals,
prefixItemstuples, and closed objects (additionalProperties: false→
Record<string, never>).
v1.12.0 — Mock server
DocTreen v1.12.0 — Mock server
Every doctreen-enabled server already exposes a full OpenAPI 3.1 document at /docs/openapi.json. v1.12 turns that into the most direct payoff possible: spin up an Express-backed fake of any spec in seconds, with realistic data, CRUD short-circuits, latency, and error injection — no separate spec-stitching, no manual handler stubs.
What's new
🎭 npx doctreen mock — spec-driven mock server. Point it at a live /docs URL or a local OpenAPI JSON file, get an Express app that serves every operation with a schema-faithful response:
# From a running doctreen-enabled server
npx doctreen mock --from http://localhost:3000/docs --port 4000
# From an OpenAPI file on disk
npx doctreen mock --from ./openapi.json --port 4000
# With latency + random declared-error injection
npx doctreen mock --from ./openapi.json --latency 100-500 --error-rate 0.1Responses come from the same schema→example generator that powers the docs UI's Copy-as-cURL and Postman buttons. $ref, oneOf / anyOf / allOf, enum, const, default, and operation-level example / examples are all respected.
🛒 In-memory CRUD short-circuits. When the spec exposes /resource plus /resource/:id, POST actually creates a row in an in-memory store, GET /resource lists, GET /resource/:id reads, PUT / PATCH mutate, DELETE removes. Envelope responses ({ products: [...], total, filters }) are detected automatically — the array is swapped in while the surrounding shape is regenerated from the schema:
curl -s http://localhost:4000/products
# → {"products":[],"total":0,"filters":{}}
curl -s -X POST -d '{"name":"Widget","price":9.99}' \
-H "Content-Type: application/json" \
http://localhost:4000/products
# → {"name":"Widget","price":9.99,"id":1,"createdAt":"…"}
curl -s http://localhost:4000/products/1
# → {"name":"Widget","price":9.99,"id":1,"createdAt":"…"}Pass --persist ./fixtures.json to save the store across restarts. Pass --no-crud if you want every endpoint to return synthesised examples instead of stateful CRUD.
🎲 Optional Faker integration. Install @faker-js/faker and fields with recognisable names (email, name, uuid, createdAt, …) plus OpenAPI format strings (uuid, email, date-time, uri, ipv4, …) automatically get realistic values:
generateExample(
{ type: 'object', properties: { email: { type: 'string', format: 'email' } } },
{ faker: true, seed: 42 }
);
// → { email: 'Aida.Effertz18@yahoo.com' }Without Faker installed, output falls back to deterministic placeholders ('string', 0, true). --no-faker forces placeholders even when Faker is present. --seed <n> makes Faker output reproducible.
⏱ Latency + error injection. Stress-test frontend error paths without touching your backend:
# Fixed 200ms delay on every response
--latency 200
# Random delay between 100 and 500ms
--latency 100-500
# Return a randomly-picked declared 4xx/5xx response 10% of the time
--error-rate 0.1Both flags compose. The selected error response is always one the spec already declares — the body is generated from its declared schema.
🧰 Public schema→example helper. The generator behind the docs UI's Copy-as-cURL and Postman buttons is now a first-class export. Use it anywhere you need a JS value from a schema:
const { generateExample } = require('doctreen/example');
generateExample(
{ type: 'object', properties: {
email: { type: 'string', format: 'email' },
createdAt: { type: 'string', format: 'date-time' },
tags: { type: 'array', items: { type: 'string' } },
}},
{ faker: true }
);Accepts both doctreen SchemaNode and OpenAPI 3.x Schema Objects. Pass components to resolve $refs.
🧪 Programmatic mock API. Embed the mock server in your own scripts or tests instead of shelling out:
const { startMockFromOpenApi } = require('doctreen/mock');
const { app, server, routeCount } = await startMockFromOpenApi({
from: './openapi.json',
port: 4000,
latency: [100, 500],
errorRate: 0.05,
persistPath: './fixtures.json',
});
// later, e.g. in afterAll()
server.close();createMockApp(...) skips the listen step if you want to mount the mock as middleware inside another Express app.
Why this matters
The natural lifecycle of a doctreen project so far has been: define routes → ship docs and OpenAPI → wire integration flows → catch drift in CI. v1.12 fills the gap on the outside-in side: frontend devs can now build against a faithful fake of the API before the backend is finished, and contract tests can run against the same fake to verify both ends agree on the shape.
The Mock Server is a peer of the existing CLI workflow:
| Command | What it does |
|---|---|
doctreen drift report --fail-on-mismatch |
Catch shape drift between code and live traffic (v1.10) |
doctreen drift reset |
Clear the drift store between CI runs (v1.10.1) |
doctreen lint openapi |
Spectral-lite linter for the exported spec (v1.11) |
doctreen mock --from <url|file> |
Serve a spec-driven fake API with CRUD + faker + injection |
Compatibility
No breaking changes for existing v1.11.x consumers. New exports (doctreen/mock, doctreen/example) are additive; @faker-js/faker is loaded lazily so it stays optional.
Upgrade
npm install doctreen@1.12.0
# optional, for richer mock data
npm install -D @faker-js/fakerv1.11.0 — OpenAPI polish
DocTreen v1.11.0 — OpenAPI polish
The OpenAPI exporter that landed in v1.7 was deliberately scrappy: schemas inlined everywhere, tags inferred from the path, no callbacks, no examples beyond a single inferred shape. v1.11 closes that gap. Every roadmap item under "OpenAPI polish" ships in this release.
What's new
🪢 components.schemas with $ref deduplication. Wrap any schema with defineSchema('Name', ...) and the exporter promotes it to components.schemas.Name automatically — every occurrence across requestBody, responses, parameters, callbacks, and webhooks turns into { $ref: '#/components/schemas/Name' }:
const { s, defineSchema } = require('doctreen');
const User = defineSchema('User', s.object({
id: s.number(),
name: s.string(),
email: s.string(),
}));
app.post('/users', defineRoute(handler, { request: { body: User }, response: User }));
app.get('/users/:id', defineRoute(handler, { response: User }));Anonymous object schemas with three or more properties that appear in two or more places also auto-promote under stable Schema1, Schema2, … names — you get the dedup benefit without naming every shape.
🏷 Per-route + top-level tags. defineRoute({ tags: [...] }) (and @DocRoute({ tags: [...] }) on NestJS) overrides the legacy first-path-segment default. Document-level metadata lives at config.openapi.tags:
expressAdapter(app, {
openapi: {
tags: [
{ name: 'users', description: 'User account management' },
{ name: 'billing', description: 'Invoices + payment methods' },
],
},
});
app.post('/users', defineRoute(handler, { tags: ['users', 'public'], /* ... */ }));Tags used by routes but not declared at the top level are auto-appended so the spec stays consistent. The new doctreen lint openapi warns about undescribed tags.
📞 OpenAPI 3.1 callbacks + webhooks. Per-operation callbacks for the spec's runtime-expression callback URLs:
app.post('/payments', defineRoute(handler, {
description: 'Create a payment',
callbacks: {
onPaymentSucceeded: {
url: '{$request.body#/callbackUrl}',
method: 'POST',
request: { body: s.object({ paymentId: s.string() }) },
response: s.object({ ok: s.boolean() }),
},
},
}));Document-level webhooks for events the server emits:
expressAdapter(app, {
openapi: {
webhooks: {
userDeleted: {
method: 'POST',
summary: 'Fired when a user closes their account',
request: { body: s.object({ userId: s.number(), deletedAt: s.string() }) },
},
},
},
});Both reuse the request/response/error pipeline — Zod or SchemaNode accepted, $ref dedup applies.
🧪 Multi-example bodies and responses.
defineRoute(handler, {
request: { body: User },
response: User,
errors: { 422: 'Validation failed' },
examples: {
request: {
basic: { value: { name: 'Ada', email: 'ada@example.com' }, summary: 'Minimum' },
admin: { value: { name: 'Boss', email: 'boss@x.com', role: 'admin' }, summary: 'With role' },
},
response: { id: 1, name: 'Ada', email: 'ada@example.com' },
responses: { 422: { value: { errors: ['email is required'] } } },
},
});Single values render as OpenAPI example; named maps render as examples. Aliases: body → request, success → response.
🩺 npx doctreen lint openapi. Spectral-lite linter for the exported (or any) OpenAPI 3.x document. Nine rules across error / warning / info:
- duplicate operationIds
- missing
info.title/info.version - undeclared path parameters (
{id}in template, missing fromparameters[]) - untagged operations / undescribed tags
- missing 4xx responses
- unused
components.schemasentries
# Live URL
npx doctreen lint openapi --url http://localhost:3000/docs
# Local file
npx doctreen lint openapi --file ./build/openapi.json --fail-on warning
# CI-friendly JSON
npx doctreen lint openapi --url https://api.example.com/docs --jsonExits 1 when the configured --fail-on threshold is reached — drop into CI alongside doctreen drift report.
Migration
No breaking changes for users.
- Specs that previously inlined the same schema multiple times now ship a single
components.schemasentry referenced by$ref. Spec validators (Spectral, Redocly) handle this natively; custom consumers reading SchemaObjects directly must follow$refs (one extra hop throughcomponents.schemas). tagFor()is no longer exported fromsrc/exporters/openapi.js; renamed todefaultTagFor(). Public API consumers reachbuildOpenApiDocumentonly, so this is invisible — but if you were importing the helper directly, rename.
What's next
- Mock server (v1.12) —
npx doctreen mockserves a fake API from the registry; Faker-backed examples;--latency,--error-rateflags;--from openapi.jsonfor spec-driven mocking - Type & client codegen (v1.13) — typed request/response interfaces and a tRPC-style fetch wrapper, in watch mode for dev
Try it
npm install doctreen@latestFull changelog: CHANGELOG.md.
Feedback welcome — open an issue.
v1.10.1 — Drift reset, daily buckets, Redis store
DocTreen v1.10.1 — Drift reset, daily buckets, Redis store
A focused follow-up to v1.10 that closes the four open items from the drift sprint. Pure additive: existing v1.10.0 servers behave identically until the new flags are flipped.
What's new
♻️ POST <docsPath>/drift/reset. Clear the in-memory drift store on demand — between integration test runs, after a deploy, or once a misbehaving client has been fixed. Opt-in and ideally protected by a shared secret:
expressAdapter(app, {
drift: {
enabled: true,
allowReset: true,
resetToken: process.env.DOCTREEN_RESET_TOKEN,
},
});# CI / cron / one-off
npx doctreen drift reset --url http://localhost:3000/docs --token "$DOCTREEN_RESET_TOKEN"
# Or directly
curl -X POST -H "x-doctreen-drift-token: $TOKEN" http://localhost:3000/docs/drift/resetWithout allowReset: true the endpoint returns 405. Without resetToken it's open — only flip that on internal-only networks. Works on all five adapters.
🛠 doctreen drift reset CLI. Companion to drift report. POSTs to /drift/reset, prints a confirmation, exits non-zero on failure. Supports --url, --token, --json.
📆 Rolling 7-day daily buckets. Alongside the existing 24-hour hourly buckets (buckets), every per-route report now carries a dailyBuckets field with the last 7 daily counts. Same sampling, no extra cost — pick whichever resolution your dashboard wants:
{
"method": "POST",
"path": "/users",
"buckets": { "2026-05-27T13": 5, "2026-05-27T14": 3 },
"dailyBuckets": { "2026-05-26": 12, "2026-05-27": 8 }
}🗄 Redis-backed DriftStore reference. A complete, multi-replica-safe implementation of the DriftStore interface ships at example/drift-redis-store.js. Bring your own ioredis/redis@4+ client; the store handles record / report / reset and survives process restarts:
const Redis = require('ioredis');
const { createRedisDriftStore } = require('doctreen/example/drift-redis-store');
const redis = new Redis(process.env.REDIS_URL);
expressAdapter(app, {
drift: {
enabled: true,
sampleRate: 0.01,
store: createRedisDriftStore({ client: redis, prefix: 'doctreen:drift:' }),
allowReset: true,
resetToken: process.env.DOCTREEN_RESET_TOKEN,
},
});Storage layout uses sets + hashes + lists with key prefixes documented in the file header. Multiple replicas can share one Redis and aggregate into the same dashboard.
Migration
No breaking changes.
- Reset endpoint defaults to disabled — existing v1.10.0 servers are unchanged until
allowResetis set. dailyBucketsis a new top-level field on every per-route report payload; consumers that only readbucketscontinue working.DriftStoreimplementations from v1.10.0 keep working —report()consumers that strictly type their schema may want to adddailyBuckets: Record<string, number>to it.
What's next
- OpenAPI polish (v1.11) — already merged:
$refdedup, first-class tags, callbacks/webhooks, multi-example,doctreen lint openapi - Mock server (v1.12) —
npx doctreen mockserves a fake API from the registry, latency/error injection,--from openapi.json
Try it
npm install doctreen@latestFull changelog: CHANGELOG.md.
Feedback welcome — open an issue.
v1.10.0 — Production-grade schema drift reporting
DocTreen v1.10.0 — Production-grade schema drift reporting
v1.5 shipped an experimental dev-only console.warn for schema drift on Express. v1.10 promotes that signal into a full reporting pipeline you can run in production: structured aggregates, opt-in sampling, pluggable storage, a UI tab, a JSON endpoint, and a CI-friendly CLI — and it works on all five adapters (Express, Fastify, Hono, Koa, NestJS).
What's new
🪡 Structured drift pipeline. A new src/internal/drift-store.js aggregates per-route counts, kind breakdowns (missing-required / unexpected-field / type-mismatch), rolling 24-hour hourly buckets, and the last N samples per route. All adapters emit through the same pipeline, so the shape of the report is identical no matter which framework you run.
⚙️ The drift config block.
expressAdapter(app, {
drift: {
enabled: true, // default: NODE_ENV !== 'production'
sampleRate: 0.01, // 1% of mismatching requests
onDrift: (event) => log.warn(event),
webhook: 'https://hooks.example.com/drift',
logLevel: 'silent', // suppress the per-process console.warn
},
});Pass false to disable, true to enable with defaults, or an object to fine-tune. The webhook fires on every recorded event; onDrift runs synchronously for in-process side effects.
📊 GET /docs/drift.json. Every adapter exposes the aggregated report at the docs path. Same shape powers the UI tab and the CLI:
{
"generatedAt": 1748371000000,
"totalIssues": 12,
"routes": [{
"method": "POST",
"path": "/users",
"total": 8,
"kinds": { "missing-required": 3, "unexpected-field": 2, "type-mismatch": 3 },
"parts": { "body": 7, "query": 1 },
"fields": { "email": 3, "age": 2 },
"buckets": { "2026-05-27T13": 5, "2026-05-27T14": 3 },
"samples": [ /* up to maxSamples recent events */ ]
}]
}🖥 UI: Drift tab. Shown in the docs UI when drift is enabled. Kind summary across the whole API, per-route cards with samples and hourly histogram, and inline DRIFT N badges on the routes table so problem routes are visible without leaving the regular view.
🔬 npx doctreen drift report CLI. New umbrella binary at bin/doctreen.js. Hits /drift.json on a running server, prints a CI-friendly table, exits 1 when --fail-on-mismatch is set and drift is present:
npx doctreen drift report --url http://localhost:3000/docs --fail-on-mismatch
# 0 routes with drift → exit 0
# 3 routes with drift → table + exit 1Drop it into integration test runs so PRs catch shape changes before they merge.
🔌 Pluggable DriftStore. The default in-memory store is fine for single-process apps. For multi-replica or long-running deployments, implement the three-method interface and pass it as drift.store:
interface DriftStore {
record(event: DriftEvent): void | Promise<void>;
report(): DriftReport | Promise<DriftReport>;
reset(): void | Promise<void>;
}A Redis-backed reference implementation lands in v1.10.1.
Migration
No breaking changes. The experimental v1.5 console.warn still fires (per unique drift signature). To unlock the full report, opt in:
expressAdapter(app, { drift: { enabled: true, sampleRate: 0.01 } });The drift hook on Fastify / Hono / Koa / NestJS — previously a roadmap item — ships in this release.
What's next
- Reset endpoint, daily buckets, Redis store reference (v1.10.1) — already in flight
- OpenAPI polish (v1.11) —
$refdedup, first-class tags, callbacks/webhooks, multi-example,doctreen lint openapi
Try it
npm install doctreen@latestFull changelog: CHANGELOG.md.
Feedback welcome — open an issue.
v1.8.0 — security schemes + hide-a-route
DocTreen v1.8.0 — security schemes + hide-a-route
v1.7 shipped a valid OpenAPI 3.1 document. v1.8 makes it a good one:
securitySchemes declared once, attached automatically, with the
Authorization header no longer awkwardly mirrored as a plain
parameter. Plus a long-requested escape hatch for endpoints that should
serve traffic but not show up in the docs.
What's new
🛡️ securitySchemes + per-route security. Declare auth schemes
once on the adapter, set a global default, override per route as
needed:
expressAdapter(app, {
openapi: {
securitySchemes: {
bearerAuth: { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
},
security: [{ bearerAuth: [] }], // applies to every route by default
},
});
// Override per route
app.post('/auth/login', defineRoute(handler, { security: [] })); // public
app.get('/admin/stats', defineRoute(handler, { security: [{ adminAuth: [] }] }));When a route has any effective security requirement, DocTreen also
strips the now-redundant Authorization header from parameters[],
so Redocly's security-defined rule passes cleanly.
🙈 hidden: true per route. Useful for internal admin tools,
experimental flags, or routes you can't remove yet:
app.get('/internal/metrics', defineRoute(handler, { hidden: true }));NestJS gains a @DocHidden() shorthand alongside the existing
@DocRoute({ hidden: true }) form.
🌐 config.openapi.servers. Point the spec at your real
environments:
expressAdapter(app, {
openapi: {
servers: [
{ url: 'https://api.example.com', description: 'Production' },
{ url: 'https://staging.api.example.com', description: 'Staging' },
],
},
});Defaults to [{ url: '/' }] (same origin as the docs page) when
omitted — Swagger UI's "Try it out" keeps working.
📚 Type surface caught up. UserConfig now declares validate
(back-filled from v1.6) and the new openapi block; RouteEntry
lists requestValidators, validateOverride, hidden, and
security; RouteRegistry gains type stubs for getVisible,
find, and findByRequestPath that have been live since v1.6/v1.8.
Migration
No breaking changes. Every new field defaults to "behave like v1.7"
when omitted. Set openapi.security: [{ bearerAuth: [] }] and you'll
notice the Authorization header drop out of the spec — that's the
intended effect, not a regression.
What's next
- Production-grade schema drift reporting — sampling, aggregation,
and a dashboard view of declared vs. observed schemas. - Drift hooks on Fastify / Hono / Koa / NestJS — port the v1.5
Express dev warning to the remaining adapters.
Try it
npm install doctreen@latestThe live demo exercises the new
fields end-to-end.
Full changelog: CHANGELOG.md.
Feedback welcome — open an issue.
v1.7.0 — OpenAPI 3.1 export
DocTreen v1.7.0 — OpenAPI 3.1 export, no extra annotations
v1.5 made one Zod schema per route the source of truth for documentation.
v1.6 made the same schema reject invalid requests. v1.7 turns the same
schema into a standards-compliant OpenAPI 3.1 document that drops into
Scalar, Redoc, Swagger UI, or any other spec-driven tool — without
writing a separate YAML file or adding a single decorator.
What's new
📜 OpenAPI 3.1 at <docsPath>/openapi.json. Every adapter — Express,
Fastify, Hono, Koa, NestJS — now ships an OpenAPI 3.1 endpoint built
from the same schemas that already drive the docs UI. Hit it with curl,
download it from the new header button, or point any spec consumer at
it directly:
curl https://your-api.example.com/docs/openapi.json > openapi.json🔗 Drops into the ecosystem you already use. The output passes
@apidevtools/swagger-cli validate, renders cleanly in
Scalar, Redoc,
and Swagger UI, and includes path parameters, query parameters, request
body with required[] arrays derived from your Zod schemas, every
declared error response, and tags grouped by the first path segment.
🖱️ One-click "Export to OpenAPI 3.1" button in the docs UI, sitting
next to "Export to Postman". Same JSON file the endpoint serves.
🛣️ Roadmap reshuffled. Validation (v1.6) and OpenAPI export (v1.7)
are both ticked. Next big items: first-class openapi.servers config +
securitySchemes so Authorization headers stop showing up as plain
parameters, and production-grade schema drift reporting.
Out of scope for now
securitySchemes/securityblocks — coming in v1.8.Authorization
and similar headers currently render asparameters[].in = header,
which is valid spec but Redocly flags as suboptimal.callbacks,webhooks,links.$ref-based schema deduplication — everything is inlined for now.
Migration
No breaking changes. The new endpoint is additive; existing routes,
adapters, and defineRoute calls are untouched. Drop in v1.7 and you
get the export for free.
Try it
npm install doctreen@latestThe live demo shows the button in the
header — click it, paste the file into your spec viewer of choice, and
the routes render with full request / response detail.
Full changelog: CHANGELOG.md.
Feedback welcome — open an issue.