API Reference

generateOpenApi

Generate an OpenAPI 3.1.0 document from a ts-kizuna contract.

Generate an OpenAPI 3.1.0 document from a contract. Returns a renderer you call with 'json' or 'yaml'.

pnpm add @ts-kizuna/openapi
bun add @ts-kizuna/openapi
npm install @ts-kizuna/openapi
import { generateOpenApi } from '@ts-kizuna/openapi/server';

Parameters

generateOpenApi(contract: Contract, overrides?: Partial<GenerateOpenApiOptions>): OpenApiRenderer
ParameterTypeDescription
contractContractThe contract from k.contract
overridesPartial<GenerateOpenApiOptions>Merged over the contract's options, for what only a build knows

It lives on @ts-kizuna/openapi/server because it reads your deprecations file from disk. The contract-facing entry, @ts-kizuna/openapi, holds openApiPlugin and the document types.

Options

Options are declared on openApiPlugin in k.ts, and generateOpenApi reads them off the contract, so a build step cannot write a document that differs from the one your server serves. Pass overrides for what only a build step knows, such as the public servers list.

OptionTypeDefaultDescription
info{ title, version, description? }requiredOpenAPI info object
openApiVersion'3.1.0''3.1.0'OpenAPI specification version for the generated document
servers{ url, description? }[]noneServer URLs
setOperationIdboolean | 'concatenated-path'falseSet operationId from the route key. Use 'concatenated-path' to include parent keys.
tagsOpenApiTag[]noneTop-level tag definitions
operationMapper(operation, route, id) => operationnoneCallback to transform each operation before it is added to the spec

There are no security options, because security is emitted from the contract automatically, so the spec can't drift from what the server enforces. See below.

Returns

An OpenApiRenderer function:

renderer('json'); // OpenApiDocument object
renderer('yaml'); // YAML string

Example

src/contract/k.ts
import { Kizuna } from '@ts-kizuna/core';
import { openApiPlugin } from '@ts-kizuna/openapi';

export const k = new Kizuna({
    plugins: {
        openApi: openApiPlugin({
            info: {
                title: 'My API',
                version: '1.0.0',
            },
            servers: [
                {
                    url: 'https://api.example.com',
                    description: 'Production',
                },
            ],
            setOperationId: true,
        }),
    },
});
import { generateOpenApi } from '@ts-kizuna/openapi/server';
import { contract } from './contract';

const spec = generateOpenApi(contract);

app.get('/openapi.json', (_req, res) => {
    res.json(spec('json'));
});

Security emission

Security is generated from the contract, not configured on the generator. Every identity registered on new Kizuna() is emitted under components.securitySchemes, and every route the auth map secures references those names in its security, with OAuth scopes where the map declares them. Public routes (false) get none.

With the user (bearer) and member (API key) identities and workspace: 'member' in the auth map, the document contains:

components:
    securitySchemes:
        user:
            type: http
            scheme: bearer
        member:
            type: apiKey
            name: x-workspace-token
            in: header
paths:
    /workspace:
        get:
            security:
                - member: []

A custom identity is the exception: OpenAPI can't describe its credential, so it emits no securityScheme and no security. Its routes instead carry an x-kizuna-guarded extension, keeping a protected-out-of-band route distinct from a public one.

Because the spec and the runtime guards both derive from the auth map, documented security is enforced security, and there is nothing to keep in sync.

See the OpenAPI guide for Scalar integration, writing specs to disk, and breaking change detection.

On this page