Advanced
Programmatic API
Use nestjs-openapi in build scripts and custom tooling
Use the programmatic API to integrate spec generation into build scripts, test suites, and custom tooling.
Basic Usage
import { } from 'nestjs-openapi';
const = await ('./openapi.config.ts');
.(`Generated: ${.}`);
.(`Paths: ${.}`);
.(`Operations: ${.}`);
.(`Schemas: ${.}`);Options
The generate() function accepts an optional second parameter for overrides:
import { generate } from 'nestjs-openapi';
const result = await generate('./openapi.config.ts', {
format: 'yaml', // Override output format
debug: true, // Enable verbose logging
});| Option | Type | Description |
|---|---|---|
format | 'json' | 'yaml' | Override the output format |
debug | boolean | Enable debug logging to stdout |
Build Script Examples
Simple Build Script
// scripts/generate-openapi.ts
import { generate } from 'nestjs-openapi';
async function main() {
console.log('Generating OpenAPI specification...');
const start = performance.now();
const result = await generate('./openapi.config.ts');
const duration = ((performance.now() - start) / 1000).toFixed(2);
console.log(`Generated ${result.outputPath} in ${duration}s`);
console.log(` ${result.pathCount} paths`);
console.log(` ${result.operationCount} operations`);
console.log(` ${result.schemaCount} schemas`);
}
main().catch((error) => {
console.error('Generation failed:', error.message);
process.exit(1);
});Run with:
npx tsx scripts/generate-openapi.tsMulti-App Build Script
// scripts/generate-all.ts
import { generate } from 'nestjs-openapi';
import { resolve } from 'path';
interface AppConfig {
name: string;
configPath: string;
}
const apps: AppConfig[] = [
{ name: 'Backend API', configPath: 'apps/backend/openapi.config.ts' },
{ name: 'Admin API', configPath: 'apps/admin/openapi.config.ts' },
{ name: 'Public API', configPath: 'apps/public/openapi.config.ts' },
];
async function generateAll() {
console.log('Generating OpenAPI specifications...\n');
const results = await Promise.all(
apps.map(async (app) => {
try {
const start = performance.now();
const result = await generate(app.configPath);
const duration = ((performance.now() - start) / 1000).toFixed(2);
return {
app: app.name,
success: true,
result,
duration,
};
} catch (error) {
return {
app: app.name,
success: false,
error: error instanceof Error ? error.message : String(error),
};
}
}),
);
// Print results
for (const r of results) {
if (r.success) {
console.log(
`${r.app}: ${r.result.operationCount} operations (${r.duration}s)`,
);
} else {
console.error(`${r.app}: FAILED - ${r.error}`);
}
}
// Exit with error if any failed
const failed = results.filter((r) => !r.success);
if (failed.length > 0) {
process.exit(1);
}
}
generateAll();Watch Mode Script
// scripts/watch-openapi.ts
import { generate } from 'nestjs-openapi';
import { watch } from 'chokidar';
const CONFIG_PATH = './openapi.config.ts';
const WATCH_PATTERNS = ['src/**/*.ts', 'openapi.config.ts'];
async function regenerate() {
console.log('\nRegenerating OpenAPI spec...');
try {
const result = await generate(CONFIG_PATH);
console.log(`Generated ${result.operationCount} operations`);
} catch (error) {
console.error(
'Generation failed:',
error instanceof Error ? error.message : error,
);
}
}
// Initial generation
regenerate();
// Watch for changes
const watcher = watch(WATCH_PATTERNS, {
ignoreInitial: true,
ignored: ['**/node_modules/**', '**/*.spec.ts'],
});
watcher.on('change', (path) => {
console.log(`File changed: ${path}`);
regenerate();
});
console.log('Watching for changes...');Integration Patterns
With Vite
// vite.config.ts
import { defineConfig, type Plugin } from 'vite';
import { generate } from 'nestjs-openapi';
function openapiPlugin(): Plugin {
return {
name: 'openapi-generate',
async buildStart() {
console.log('Generating OpenAPI spec...');
await generate('./openapi.config.ts');
},
};
}
export default defineConfig({
plugins: [openapiPlugin()],
});With esbuild
// build.ts
import { build } from 'esbuild';
import { generate } from 'nestjs-openapi';
async function main() {
// Generate OpenAPI spec first
console.log('Generating OpenAPI spec...');
await generate('./openapi.config.ts');
// Then build
console.log('Building application...');
await build({
entryPoints: ['src/main.ts'],
bundle: true,
outdir: 'dist',
platform: 'node',
format: 'esm',
});
}
main();With Jest/Vitest
// tests/openapi.test.ts
import { generate } from 'nestjs-openapi';
import { readFileSync } from 'fs';
describe('OpenAPI Specification', () => {
let spec: any;
beforeAll(async () => {
await generate('./openapi.config.ts');
spec = JSON.parse(readFileSync('openapi.json', 'utf-8'));
});
it('should have correct info', () => {
expect(spec.info.title).toBe('My API');
expect(spec.info.version).toBe('1.0.0');
});
it('should have expected endpoints', () => {
expect(spec.paths['/api/users']).toBeDefined();
expect(spec.paths['/api/users'].get).toBeDefined();
expect(spec.paths['/api/users'].post).toBeDefined();
});
it('should have schemas for DTOs', () => {
expect(spec.components.schemas.CreateUserDto).toBeDefined();
expect(spec.components.schemas.UserDto).toBeDefined();
});
it('should have security schemes', () => {
expect(spec.components.securitySchemes.bearerAuth).toBeDefined();
});
});With Semantic Release
// release.config.ts
import { generate } from 'nestjs-openapi';
export default {
plugins: [
'@semantic-release/commit-analyzer',
'@semantic-release/release-notes-generator',
[
'@semantic-release/exec',
{
prepareCmd: `
npx tsx -e "
import { generate } from 'nestjs-openapi';
await generate('./openapi.config.ts');
"
`,
},
],
[
'@semantic-release/git',
{
assets: ['openapi.json'],
message: 'chore(release): update OpenAPI spec [skip ci]',
},
],
],
};Error Handling
Basic Error Handling
import { generate } from 'nestjs-openapi';
try {
const result = await generate('./openapi.config.ts');
console.log('Success:', result.outputPath);
} catch (error) {
if (error instanceof Error) {
console.error('Failed:', error.message);
}
process.exit(1);
}Typed Error Handling
import { } from 'nestjs-openapi';
import {
,
,
,
,
} from 'nestjs-openapi';
try {
await ('./openapi.config.ts');
} catch () {
if ( instanceof ) {
.('Config file not found');
} else if ( instanceof ) {
.('Failed to load config file');
} else if ( instanceof ) {
.('Invalid configuration');
} else if ( instanceof ) {
.(`Entry module not found: ${.}`);
} else {
throw ;
}
.(1);
}Accessing Generated Spec
Read the generated spec after generation:
import { generate } from 'nestjs-openapi';
import { readFileSync } from 'fs';
import type { OpenApiSpec } from 'nestjs-openapi';
async function main() {
const result = await generate('./openapi.config.ts');
// Read the generated spec
const spec: OpenApiSpec = JSON.parse(
readFileSync(result.outputPath, 'utf-8'),
);
// Process the spec
console.log('Endpoints:');
for (const [path, methods] of Object.entries(spec.paths)) {
for (const method of Object.keys(methods)) {
console.log(` ${method.toUpperCase()} ${path}`);
}
}
}
main();See Also
- generate() - API reference
- Effect API - Effect-TS integration
- CI/CD Recipe - Automation patterns