@grest-ts/api-docs
The native documentation UI for grest-ts services. Renders contracts directly — no conversion to OpenAPI or AsyncAPI in the rendering path. Brand intersection types, typed errors, request/response/event patterns, cross-method reuse detection, all surfaced honestly from your
GGContractClassdefinitions.
🚀 Open the live demo → The actual UI rendered against the grest-test example service. Try the sidebar, brand highlighting, discriminated unions, Schema/Example view toggle.
A typical grest-ts service has both HTTP and WebSocket APIs, with rich types like branded primitives, discriminated unions, and shared response shapes. Generic OpenAPI/AsyncAPI tooling flattens most of that into spec-language. This package renders your contracts as they actually exist — string & UserId, req/event patterns, IN/OUT for WS direction, "↔ N" reuse chips when the same shape flows through multiple methods.
Features
- Native React UI, served from a single bundle (~70 KB gzipped). Pre-built and shipped with the package — users do not rebuild.
- Live mode (
GGApiDocs.register) mounts the UI + a contract document JSON endpoint on your runtime's HTTP server. - Static mode (
buildApiDocs) writes the same UI + JSON to a directory — drop on S3 / GitHub Pages / Cloudflare Pages, no rewriting needed. - Brand types as
string & BrandNameintersections — no information lost, the underlying primitive stays visible. - HTTP + WebSocket in one page —
GET /api/users/:idand[CLIENT][SENDS TO][SERVER] ws/chatrendered with the same vocabulary, color-coded distinctions for direction (INindigo /OUTorange) and pattern (req/event). - Typed error responses — every
errors: [VALIDATION_ERROR, ...]declaration becomes its own response card, with the error data shape rendered alongside. - Reuse detection — when the same
const X = IsObject({...})(or any branded type) is referenced from multiple methods, an↔ Nchip surfaces in the schema view. Click → popover lists every other method using it. cmd/ctrl-click opens new tab. Highlight follows you across navigation. - Three navigation lenses — sidebar tabs: Groups (your APIs as you composed them), Brands (alphabetical brand index), and inline reuse chips (per-schema cross-references).
- Stable JSON contract document — the underlying
ApiDocsDocumentformat is exported; drives the UI but is also useful for SDK pipelines, AI tooling, or anything that wants a richer view of your contracts than OpenAPI provides.
Installation
npm install @grest-ts/api-docsLive mode — GGApiDocs.register()
Drop one call into your runtime's compose(). Every distributed system tends to expose more than one API surface (public, admin, internal callbacks) — docs[] lets you mount them side-by-side under a single mount path with a header dropdown to switch:
import {GGApiDocs} from "@grest-ts/api-docs"
import {GGHttpServer} from "@grest-ts/http"
protected compose(): void {
new GGHttpServer()
UserApi.register(new UserApiImpl())
OrderApi.register(new OrderApiImpl())
ChatApiSchema.register(new ChatHandler())
GGApiDocs.register({
docsPath: "/docs",
docs: [
{
slug: "platform",
title: "MyOrg Platform",
version: "1.0.0",
groups: {
"Users": {http: [UserApi]},
"Orders": {http: [OrderApi]},
"Realtime": {ws: [ChatApiSchema]},
},
},
{
slug: "internal",
title: "Internal Callbacks",
groups: {
"Webhooks": {http: [WebhookApi]},
},
},
],
})
}Routes mounted under docsPath:
| URL | Purpose |
|---|---|
GET /docs | React UI shell (HTML), with the doc-switcher dropdown |
GET /docs/<slug>/api-docs.json | The contract document for a single doc (lazy build, cached) |
GET /docs/assets/* | Pre-built JS/CSS bundle (~70 KB gzipped, served offline) |
The first entry in docs[] is the default selection. Deep links use the form #/<slug>/<group>/<contract>/<method> — paste any one of those into the URL bar and the dropdown follows.
Single doc
The same call with one entry — no dropdown is rendered:
GGApiDocs.register({
docsPath: "/docs",
docs: [{slug: "api", title: "My App", http: [ItemApi, OrderApi], ws: [ChatApiSchema]}],
})
// One "API" group with the schemas split by HTTP / WebSocket; no dropdown.Ungrouped shorthand
Each ApiDocSpec accepts top-level http/ws arrays as a shortcut for a single auto-named "API" group, just like a groups map with one entry:
GGApiDocs.register({
docsPath: "/docs",
docs: [{slug: "api", title: "My App", http: [ItemApi, OrderApi], ws: [ChatApiSchema]}],
})Branding
Light visual customization shared across all docs:
GGApiDocs.register({
docsPath: "/docs",
branding: {
logoUrl: "/static/logo.svg",
primaryColor: "#1d4ed8",
},
docs: [{slug: "platform", title: "MyOrg Platform", groups: { /* ... */ }}],
})Static mode — buildApiDocs()
Build a complete static site to disk — no runtime required. Same docs[] shape as the live mode:
import {buildApiDocs} from "@grest-ts/api-docs"
await buildApiDocs({
outDir: "./dist/docs",
docs: [
{
slug: "platform",
title: "MyOrg Platform",
groups: {
"Users": {http: [UserApi]},
"Orders": {http: [OrderApi]},
"Realtime": {ws: [ChatApiSchema]},
},
},
],
})Output:
dist/docs/
├── index.html ← shell, references ./assets/* + ./<slug>/api-docs.json (relative)
├── platform/
│ └── api-docs.json
└── assets/
└── index-[hash].{js,css}The shell uses relative URLs, so the directory drops onto any static host at any path prefix without rewriting.
API reference
GGApiDocs.register(options: GGApiDocsOptions): void
new GGApiDocs(server: GGHttpServer, options: GGApiDocsOptions)
await buildApiDocs(options: BuildApiDocsOptions): Promise<void>
// Lower-level: just the contract document, no UI — useful for SDK pipelines or custom renderers
buildContractDoc(options: BuildContractDocOptions): ApiDocsDocumentSee index-node.ts for the full type surface.
When NOT to use this package
- You specifically need OpenAPI tooling (Postman, openapi-generator, contract testing) — install
@grest-ts/openapiinstead (or alongside). - You specifically need AsyncAPI tooling — install
@grest-ts/asyncapi. - You want vanilla Swagger UI / AsyncAPI Studio for some other reason — those ship with the openapi/asyncapi packages respectively.
If you want a polished docs UI for your grest-ts service that surfaces what your contracts actually contain, this is the right package.
