openApiPlugin

Serve your OpenAPI document and an API reference UI for it, on any adapter.

Write the OpenAPI document generated from your API on kizuna generate, and serve it with a reference UI. Name it under plugins on defineConfig and api.mount serves its routes. See the OpenAPI guide.

pnpm add @kizunajs/openapi@beta
import { openApiPlugin } from '@kizunajs/openapi';

Parameters

openApiPlugin(props: OpenApiPluginProps): PluginDeclaration
ParameterTypeRequiredDescription
propsOpenApiPluginPropsYesEverything generateOpenApi takes, plus the options below

Props

OpenApiPluginProps extends GenerateOpenApiOptions, so info, servers, setOperationId and the rest work exactly as they do on generateOpenApi. On top of those:

OptionDefaultDescription
slug'openApi'What handlers reach this plugin under
outputnoneWhere kizuna generate writes the document
includeHiddenfalseDocument hidden routes too
docsPathoffThe reference UI. Give it a path to serve it
jsonPathoffThe document. A path ending in .json
yamlPathoffThe document as YAML. A path ending in .yaml
provider'scalar'Which reference UI to render, 'scalar' or 'swagger'
cdnUrlthe provider's CDNWhere to load the UI's assets from
pageTitlethe document titleThe page's <title>
configurationnoneExtra config merged into the UI's initializer

They are also the options generateOpenApi uses when you call it without any, so a build step and the served document cannot disagree.

Each path prop is off until you give it a path. The document paths carry their extension in the type, so jsonPath: '/docs' does not compile.

Example

kizuna.config.ts
import { defineConfig } from 'kizunajs';
import { expressAdapter } from '@kizunajs/express';
import { openApiPlugin } from '@kizunajs/openapi';
import { routes } from './src/routes';

export default defineConfig({
    adapter: expressAdapter(),
    routes,
    plugins: [
        openApiPlugin({
            info: {
                title: 'My API',
                version: '1.0.0',
            },
            docsPath: '/docs',
            setOperationId: true,
        }),
    ],
});
src/index.ts
import kizuna from '../kizuna.config';

kizuna.api.mount(app);

Two documents

A plugin installs once, so a second document goes under documents. Each entry takes the same options as the plugin, and the plugin's own values are its defaults:

kizuna.config.ts
openApiPlugin({
    info: {
        title: 'My API',
        version: '1.0.0',
    },
    documents: [
        {
            output: './openapi.yaml',
            docsPath: '/docs',
        },
        {
            info: {
                title: 'My API, internal',
                version: '1.0.0',
            },
            output: './openapi.internal.yaml',
            docsPath: '/internal/docs',
            includeHidden: true,
        },
    ],
}),

Plugin routes

These are plugin routes marked hidden, so your fetch client, the generated Swift and Kotlin clients, and the document itself never list them.

Public by default

Nothing guards these routes. Gate them with your framework's own middleware if your document should not be public.

Air-gapped and strict CSP

cdnUrl points the page at a self-hosted copy of the UI's assets. For 'scalar' it is the script URL; for 'swagger' it is the directory holding swagger-ui.css and swagger-ui-bundle.js.

Serving it yourself

generateOpenApi is exported, so you can skip the plugin and serve the document from your own route. Mixing works: leave a path unset and the plugin stays off it. Set jsonPath but not docsPath to serve the document while rendering your own UI.