@contract-first-api/openapi
v1.0.1
Published
Generate OpenAPI documents from contract-first API contract trees.
Downloads
157
Maintainers
Readme
@contract-first-api/openapi
Generate an OpenAPI document from a shared contract tree.
This package consumes contracts from @contract-first-api/core and turns them
into a plain OpenAPI document object. It does not write files, register routes,
serve Swagger UI, or choose a documentation frontend. You decide whether to
write the document to disk, expose it from your backend, or pass it to another
tool.
Install
pnpm add @contract-first-api/openapiCreate A Document
import { createOpenApiDocument } from "@contract-first-api/openapi";
import { contracts } from "@example/shared";
export const openApiDocument = createOpenApiDocument(contracts, {
info: {
title: "Todo API",
version: "1.0.0",
},
servers: [{ url: "http://localhost:3000/api" }],
});The returned value is a normal object. You can write it to a file:
import { writeFileSync } from "node:fs";
import { openApiDocument } from "./openapi.ts";
writeFileSync("openapi.json", JSON.stringify(openApiDocument, null, 2));Or expose it from your backend:
app.get("/openapi.json", (_req, res) => {
res.json(openApiDocument);
});Contract Mapping
The generator walks the contract tree and creates one OpenAPI operation for each JSON contract.
pathvalues like/todos/:idbecome/todos/{id}methodbecomes the OpenAPI operation methodrequest.paramsbecomes path parametersrequest.querybecomes query parametersrequest.bodybecomes a JSON request bodyresponsebecomes the success response schemasuccessStatusCodebecomes the success response status when providederrorsbecome additional response entries
Only JSON contracts are included in the generated document.
Zod JSON Schema
Schemas are converted with Zod 4's built-in z.toJSONSchema() support. Request
schemas use input mode, and response schemas use output mode.
By default, unrepresentable Zod schemas throw during document generation. This keeps the generated document from silently describing something less precise than the contract.
createOpenApiDocument(contracts, {
info: {
title: "Todo API",
version: "1.0.0",
},
schema: {
unrepresentable: "any",
},
});Customizing The Document
Use transformOperation for route-level OpenAPI fields such as summary,
tags, security, or vendor extensions.
const document = createOpenApiDocument(contracts, {
info: {
title: "Todo API",
version: "1.0.0",
},
transformOperation: ({ contract, operation }) => ({
...operation,
...(contract.meta?.requiresAuth
? { security: [{ bearerAuth: [] }] }
: {}),
}),
});Use transformDocument for top-level fields and shared components.
const document = createOpenApiDocument(contracts, {
info: {
title: "Todo API",
version: "1.0.0",
},
transformDocument: (document) => ({
...document,
components: {
...document.components,
securitySchemes: {
bearerAuth: {
type: "http",
scheme: "bearer",
},
},
},
}),
});