Skip to main content

Generating OpenAPI Documents

OpenApiDocumentFactory is the single entry point. It accepts an ApiDocument and returns an OpenAPI document as a plain JavaScript object — no file I/O, no side effects.


generate(document, options?)

Returns the OpenAPI document directly.

import { OpenApiDocumentFactory } from '@opra/openapi';

const openApiDoc = OpenApiDocumentFactory.generate(apiDocument, {
version: '3.1', // '3.0' (default) or '3.1'
});

Options

OptionTypeDefaultDescription
version'3.0' | '3.1''3.0'Target OpenAPI spec version. Produces 3.0.3 or 3.1.0 in the openapi field.
scopestringundefinedWhen set, only types, fields, and parameters that pass .inScope(scope) are included. Useful for generating separate public / internal / admin API specs from the same document.

generateWithWarnings(document, options?)

Same as generate() but also returns a warnings array — a list of OPRA constructs that have no exact OpenAPI equivalent and were either skipped or approximated.

const { document, warnings } = OpenApiDocumentFactory.generateWithWarnings(apiDocument);

if (warnings.length) {
console.warn('OpenAPI generation warnings:');
warnings.forEach(w => console.warn(' •', w));
}

What triggers a warning

  • QUERY and SEARCH methods — these HTTP methods are not part of the OpenAPI 3.x path item spec and cannot be represented; those operations are skipped.
  • Duplicate operations — when two operations resolve to the same METHOD /path combination, only the first one is kept and a warning is recorded.
  • Unknown DataType kinds — any custom DataType subclass that isn't ComplexType, MappedType, MixinType, EnumType, ArrayType, UnionType, or SimpleType is mapped to an empty schema {}.

Version differences

Feature3.03.1
openapi field value3.0.33.1.0
Nullable fieldsnullable: true alongside typetype: ['string', 'null'] (JSON Schema–style)

Both versions are generated from the same input; only the nullable representation differs in the output.


Serving the document

The output is a plain, JSON-serialisable object. How you serve it is up to you — use any HTTP framework:

import express from 'express';
import { OpenApiDocumentFactory } from '@opra/openapi';

const app = express();

app.get('/openapi.json', (_req, res) => {
const doc = OpenApiDocumentFactory.generate(apiDocument);
res.json(doc);
});

Serving with Swagger UI (Express)

npm install swagger-ui-express
import swaggerUi from 'swagger-ui-express';
import { OpenApiDocumentFactory } from '@opra/openapi';

const spec = OpenApiDocumentFactory.generate(apiDocument);

app.use('/docs', swaggerUi.serve, swaggerUi.setup(spec));

Serving with Scalar (Express)

npm install @scalar/express-api-reference
import { apiReference } from '@scalar/express-api-reference';
import { OpenApiDocumentFactory } from '@opra/openapi';

const spec = OpenApiDocumentFactory.generate(apiDocument);

app.use('/docs', apiReference({ content: spec }));

NestJS integration

In a NestJS application, call OpenApiDocumentFactory after the app is initialised, then serve the result:

import { NestFactory } from '@nestjs/core';
import { OpraHttpModule } from '@opra/nestjs-http';
import { OpenApiDocumentFactory } from '@opra/openapi';
import swaggerUi from 'swagger-ui-express';
import { AppModule } from './app.module.js';

async function bootstrap() {
const app = await NestFactory.create(AppModule);

// Obtain the ApiDocument from the OPRA NestJS module
const opraModule = app.get(OpraHttpModule);
const { document, warnings } = OpenApiDocumentFactory.generateWithWarnings(
opraModule.document,
{ version: '3.1' },
);

if (warnings.length) warnings.forEach(w => console.warn(w));

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(document));

await app.listen(3000);
}
bootstrap();

Writing to a file

To save the spec to disk (useful for CI validation, versioned spec files, or client code generation pipelines):

import { writeFileSync } from 'node:fs';
import { OpenApiDocumentFactory } from '@opra/openapi';

const doc = OpenApiDocumentFactory.generate(apiDocument);
writeFileSync('openapi.json', JSON.stringify(doc, null, 2));

Scope filtering

When your ApiDocument is shared between multiple audiences — public API, internal API, admin API — use the scope option to produce separate specs:

// Fields and parameters decorated with @ApiField({ scope: 'internal' })
// will only appear in the internal spec.

const publicSpec = OpenApiDocumentFactory.generate(apiDocument, {
scope: 'public',
});

const internalSpec = OpenApiDocumentFactory.generate(apiDocument, {
scope: 'internal',
});
note

Scope filtering is applied at the type-system level by OPRA's DataType.inScope() — it is the same mechanism used across the whole OPRA stack, not an OpenAPI-specific feature.


Type Mapping