Schema to OpenAPI mapping
ZinTrust generates an OpenAPI 3.0.3 document at runtime from the registered routes plus each route’s metadata.
The generator is intentionally pragmatic: it aims to produce a useful spec that matches how requests are validated and how handlers are shaped, but it does not try to perfectly model every runtime behavior.
Core implementation lives in src/openapi/OpenApiGenerator.ts.
What the generator consumes
OpenAPI output is driven by the route records stored in RouteRegistry.
- Route registration happens through the
Router.*helpers. - Each registration records: HTTP method, path, tags, middleware names, and (optionally) schemas.
The generator uses that registry to build:
pathsandoperations(per method)parameters(path/query/header)requestBodyresponses
Path normalization
ZinTrust uses Express-style route params (e.g. /:id).
OpenAPI requires {id} placeholders.
The generator normalizes paths by converting /:param to /{param} and then uses the normalized path as the OpenAPI paths key.
operationId generation
To keep operationId stable and deterministic, the generator derives it from:
- the HTTP method
- the normalized path
This avoids accidental churn in generated client SDKs.
Parameters
ZinTrust supports generating OpenAPI parameters from route schemas.
Path parameters
If the route declares a params/path schema, the generator emits OpenAPI parameters with:
in: "path"required: truename: <paramName>
Important: OpenAPI requires that every {param} in the path has a corresponding path parameter. If you use route params but omit a params schema, your spec may be incomplete.
Query parameters
If the route declares a query schema, its properties are emitted as:
in: "query"requiredderived from schema requirements
Header parameters
If the route declares a headers schema, its properties are emitted as:
in: "header"
Notes:
- Header names are treated as case-insensitive by HTTP, but OpenAPI parameter names are case-sensitive strings; use consistent casing.
- Some headers are typically set by middleware/proxies (e.g. auth headers); documenting them is still useful.
Request body
If the route declares a body schema, the generator emits a JSON requestBody:
content["application/json"].schemais derived from the body schemarequireddepends on how the schema is declared
If your route accepts other content types (multipart, form-encoded, etc.), you must document that yourself; the built-in generator currently focuses on JSON.
Responses
Routes can declare response schemas and status codes.
The generator maps these into responses entries. If a schema is present, it is emitted as JSON content:
content["application/json"].schemaderived from the response schema
If a route doesn’t provide explicit response metadata, the generator will still emit a response entry so the operation isn’t “schema-less”, but it may be generic.
Schema conversion (high level)
ZinTrust’s validation system is richer than vanilla JSON Schema; OpenAPI uses a JSON Schema dialect.
The generator therefore does a best-effort conversion from ZinTrust schema objects into OpenAPI-compatible schema fragments.
In practice:
- primitives become
type: string|number|integer|boolean - objects become
type: objectwithproperties+required - arrays become
type: arraywithitems
Limitations and gotchas
These are the most common mismatches between runtime behavior and what the OpenAPI generator can express:
- Runtime-only validation: custom validators, cross-field rules, or dynamic rules may not be representable.
- Coercion and parsing: if middleware parses/coerces values (e.g. strings to numbers), OpenAPI may still show the original wire type unless your schema encodes it.
- Unions/discriminators: complex union types may degrade to looser schemas.
- Non-JSON bodies: multipart uploads and form bodies require manual documentation.
- Auth/permissions: middleware-enforced auth/roles are not automatically modeled as OpenAPI
securityrequirements unless you encode them in route metadata.
Recommendations
Always provide a params schema for routes that use
/:paramsegments.Treat the generated spec as a living artifact: keep route schemas close to handlers so docs don’t drift.
If you need strict OpenAPI output for SDK generation, validate
/openapi.jsonin CI and add targeted metadata for edge cases.required→ field listed inrequired[]string→{ type: 'string' }email→{ type: 'string', format: 'email' }uuid→{ type: 'string', format: 'uuid' }url→{ type: 'string', format: 'uri' }integer/digits→{ type: 'integer' }number/decimal→{ type: 'number' }min/max→minimum/maximumminLength/maxLength→minLength/maxLengthregex→patternin([...])→enum(JSON primitives only)
Limitations
- Array element typing is not currently inferred (arrays become
items: {}). - Deep/nested object schemas are not automatically derived.
- Response schemas are only included if you set
meta.response.schema.