@routegraph/docs
v1.0.0
Published
Self-contained interactive docs UI for RouteGraph, plus static export — no React, no CDN, no build step.
Readme
@routegraph/docs
Self-contained, interactive API documentation for RouteGraph — no React, no CDN, no build step. Renders directly from your routes' Zod schemas.
Installation
pnpm add -D @routegraph/docszod is a peer dependency (^4.0.0 — narrower than @routegraph/core's range, since this package imports Zod's exported classes as runtime values for schema rendering; see DECISIONS.md).
createDocsMiddleware(graph, options?)
function createDocsMiddleware(graph: RouteGraph, options?: DocsMiddlewareOptions): DocsMiddleware
// DocsMiddleware = (req: { method?; url? }, res: { statusCode; setHeader; end }, next: () => void) => void
interface DocsMiddlewareOptions {
basePath?: string // where the adapter's router is actually mounted, e.g. '/api' — used only
// so the UI's Try It Out panel builds correct request URLs
}createDocsMiddleware returns a plain Connect-style middleware — it works with any framework whose request/response objects structurally match { method?, url? } / { statusCode, setHeader, end }, which Node's raw IncomingMessage/ServerResponse satisfy directly.
Usage with each adapter
Express / Koa / Fastify (via request.raw/reply.raw, which are Node's native req/res):
// Express
app.use('/_routegraph', createDocsMiddleware(graph, { basePath: '/api' }))
// Fastify — hijack the reply so Fastify doesn't also try to send its own response
app.get('/_routegraph/*', async (request, reply) => {
reply.hijack()
docsMiddleware(request.raw, reply.raw, () => {})
})
// Koa
app.use(async (ctx, next) => {
if (!ctx.path.startsWith('/_routegraph')) return next()
await new Promise<void>((resolve) => docsMiddleware({ method: ctx.req.method, url: ctx.req.url }, ctx.res, resolve))
})Hono / Elysia (no raw Node req/res — bridge manually):
function createHonoDocsMiddleware(graph: RouteGraph, options?: DocsMiddlewareOptions) {
const docsMiddleware = createDocsMiddleware(graph, options)
return async (c: Context, next: Next) => {
let status = 200, headers = new Headers(), body: string | undefined, skipped = false
docsMiddleware(
{ method: c.req.method, url: c.req.path },
{
get statusCode() { return status }, set statusCode(v) { status = v },
setHeader: (k, v) => headers.set(k, v),
end: (chunk) => { body = chunk },
},
() => { skipped = true }
)
if (skipped) return next()
c.res = new Response(body ?? null, { status, headers })
}
}
app.use('/_routegraph/*', createHonoDocsMiddleware(graph, { basePath: '/api' }))See examples/with-express/index.hono.ts and index.elysia.ts for the complete, working versions of this bridge. routegraph dev mounts docs automatically for Express, Fastify, and Koa; for Hono and Elysia you currently need to wire it yourself, as above.
exportDocs(graph, outDir, options?)
function exportDocs(graph: RouteGraph, outDir: string, options?: DocsMiddlewareOptions): Promise<void>Writes a single self-contained outDir/index.html — the route payload is inlined as a <script> data block rather than fetched live, so the file works when opened offline or hosted as a static asset with no server behind it. routegraph export-docs (no --format flag, or --format ui) calls this.
renderSchema(schema)
function renderSchema(schema: ZodTypeAny): SchemaNodeWalks a Zod schema (via instanceof checks against Zod's exported classes — ZodString, ZodObject, ZodOptional, etc.) into a plain, serializable tree the UI renders as text:
renderSchema(z.object({ id: z.string().uuid(), role: z.enum(['admin', 'user']).optional() }))
// {
// type: 'object',
// fields: {
// id: { type: 'string', format: 'uuid' },
// role: { type: 'enum', enum: ['"admin"', '"user"'], optional: true },
// },
// }This is a separate, independent implementation from @routegraph/core's zod-to-jsonschema.ts (used for OpenAPI export) — see DECISIONS.md for why both exist.
What the UI shows
- Sidebar: a search box, method filter chips (
ALL/GET/POST/PUT/PATCH/DELETE), and the route list grouped by each route'stags(untagged routes group under "Untagged", sorted last). - Detail panel: method + path, description, tags, a deprecation banner for routes with
deprecated: true, tabbed request schema (params/query/body/headers — whichever are declared) and tabbed response schema (by status code), each rendered as a type tree. - Try It Out: a form (base URL, path params, query params, JSON body for
POST/PUT/PATCH, headers) that fires a realfetch()against your running server and displays status, timing, and the response body, with copy/clear actions.
Dark/light mode
A theme toggle in the navbar persists the choice to localStorage (routegraph-theme) and defaults to the OS prefers-color-scheme on first load.
Production warning
The docs UI is a development tool — it exposes your full route list, request/response schemas, and a live Try It Out panel that can fire real requests against your server. Gate createDocsMiddleware() behind an environment check (process.env.NODE_ENV === 'development' or equivalent) rather than mounting it unconditionally in production.
