OpenAPI (Swagger) module for Nest framework enabling automatic API documentation generation from TypeScript decorators
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
The SwaggerModule class is the main integration point between NestJS applications and Swagger UI. It provides static methods for creating OpenAPI documents from your NestJS application metadata and setting up interactive Swagger UI documentation interfaces.
import { SwaggerModule } from '@nestjs/swagger';
class SwaggerModule {
static createDocument(
app: INestApplication,
config: Omit<OpenAPIObject, 'paths'>,
options?: SwaggerDocumentOptions
): OpenAPIObject;
static setup(
path: string,
app: INestApplication,
documentOrFactory: OpenAPIObject | (() => OpenAPIObject),
options?: SwaggerCustomOptions
): void;
static loadPluginMetadata(
metadataFn: () => Promise<Record<string, any>>
): Promise<void>;
}static createDocument(
app: INestApplication,
config: Omit<OpenAPIObject, 'paths'>,
options?: SwaggerDocumentOptions
): OpenAPIObjectCreates a complete OpenAPI document by scanning the NestJS application for controllers, decorators, and metadata, then combining it with the provided configuration.
Parameters:
app: The NestJS application instanceconfig: OpenAPI document configuration (typically from DocumentBuilder)options: Optional document generation optionsType Definitions:
interface SwaggerDocumentOptions {
include?: Function[];
extraModels?: Function[];
ignoreGlobalPrefix?: boolean;
deepScanRoutes?: boolean;
operationIdFactory?: OperationIdFactory;
linkNameFactory?: (controllerKey: string, methodKey: string, fieldKey: string) => string;
autoTagControllers?: boolean;
}
type OperationIdFactory = (
controllerKey: string,
methodKey: string,
version?: string
) => string;Basic Usage:
import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('My API')
.setDescription('API description')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
// Document now contains all paths, schemas, and operations
console.log(document.paths); // All endpoint definitions
console.log(document.components); // All schema definitions
}Advanced Usage with Options:
import { ProductModule, UserModule } from './modules';
import { BaseDto, ErrorDto } from './common/dto';
const document = SwaggerModule.createDocument(app, config, {
// Only include specific modules
include: [ProductModule, UserModule],
// Add extra models that aren't automatically discovered
extraModels: [BaseDto, ErrorDto],
// Scan imported modules deeply
deepScanRoutes: true,
// Custom operation ID generation
operationIdFactory: (controllerKey, methodKey, version) => {
return `${controllerKey.toLowerCase()}_${methodKey}_v${version || '1'}`;
},
// Custom link name generation for OpenAPI links
linkNameFactory: (controllerKey, methodKey, fieldKey) => {
return `link_${controllerKey}_${methodKey}_${fieldKey}`;
},
// Disable automatic tag generation from controller names
autoTagControllers: false,
// Ignore global prefix for this documentation
ignoreGlobalPrefix: true
});static setup(
path: string,
app: INestApplication,
documentOrFactory: OpenAPIObject | (() => OpenAPIObject),
options?: SwaggerCustomOptions
): voidSets up Swagger UI at the specified path, serving both the interactive documentation interface and the raw OpenAPI specification in JSON/YAML formats.
Parameters:
path: URL path where Swagger UI will be servedapp: The NestJS application instancedocumentOrFactory: OpenAPI document or factory function to generate itoptions: Extensive customization options for Swagger UIType Definitions:
interface SwaggerCustomOptions {
useGlobalPrefix?: boolean;
ui?: boolean;
raw?: boolean | Array<'json' | 'yaml'>;
swaggerUrl?: string;
jsonDocumentUrl?: string;
yamlDocumentUrl?: string;
patchDocumentOnRequest?: <TRequest = any, TResponse = any>(
req: TRequest,
res: TResponse,
document: OpenAPIObject
) => OpenAPIObject;
explorer?: boolean;
swaggerOptions?: SwaggerUiOptions;
customCss?: string;
customCssUrl?: string | string[];
customJs?: string | string[];
customJsStr?: string | string[];
customfavIcon?: string;
customSiteTitle?: string;
customSwaggerUiPath?: string;
}Basic Setup:
const config = new DocumentBuilder()
.setTitle('E-Commerce API')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
// Serve Swagger UI at /api-docs
SwaggerModule.setup('api-docs', app, document);
// Now available at:
// http://localhost:3000/api-docs - Swagger UI interface
// http://localhost:3000/api-docs-json - JSON specification
// http://localhost:3000/api-docs-yaml - YAML specificationAdvanced Customization:
SwaggerModule.setup('api-docs', app, document, {
// Use global prefix if app.setGlobalPrefix() was called
useGlobalPrefix: true,
// Customize document URLs
jsonDocumentUrl: '/api/v1/spec.json',
yamlDocumentUrl: '/api/v1/spec.yaml',
// Enable OpenAPI definition selector in UI
explorer: true,
// Swagger UI customization
swaggerOptions: {
// Keep authorization when page refreshes
persistAuthorization: true,
// Display request duration
displayRequestDuration: true,
// Expand operations by default
docExpansion: 'list',
// Try it out enabled by default
tryItOutEnabled: true,
// Show/hide request examples
requestInterceptor: (req) => {
console.log('Request:', req);
return req;
}
},
// Custom styling
customSiteTitle: 'E-Commerce API Documentation',
customfavIcon: '/favicon.ico',
customCss: `
.topbar-wrapper { display: none; }
.swagger-ui .info { margin: 20px 0; }
`,
customCssUrl: [
'https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap'
],
// Dynamic document patching per request
patchDocumentOnRequest: (req, res, document) => {
// Add user-specific info or filter content based on request
const userRole = req.headers['x-user-role'];
if (userRole !== 'admin') {
// Remove admin-only paths
const filteredPaths = {};
Object.keys(document.paths).forEach(path => {
if (!path.includes('/admin/')) {
filteredPaths[path] = document.paths[path];
}
});
document.paths = filteredPaths;
}
return document;
}
});Document Factory Pattern:
// Use factory for dynamic document generation
SwaggerModule.setup('api-docs', app, () => {
const config = new DocumentBuilder()
.setTitle('Dynamic API')
.setVersion(process.env.API_VERSION || '1.0')
.setDescription(`Built at: ${new Date().toISOString()}`)
.build();
return SwaggerModule.createDocument(app, config);
}, {
customSiteTitle: 'Dynamic API Documentation'
});// Serve only Swagger UI (no raw JSON/YAML endpoints)
SwaggerModule.setup('docs', app, document, {
ui: true,
raw: false
});
// Serve only JSON specification (no UI or YAML)
SwaggerModule.setup('docs', app, document, {
ui: false,
raw: ['json']
});
// Serve UI and only YAML specification
SwaggerModule.setup('docs', app, document, {
ui: true,
raw: ['yaml']
});
// Disable everything (useful for conditional setup)
if (process.env.NODE_ENV !== 'production') {
SwaggerModule.setup('docs', app, document, {
ui: true,
raw: true
});
} else {
SwaggerModule.setup('docs', app, document, {
ui: false,
raw: false
});
}static loadPluginMetadata(
metadataFn: () => Promise<Record<string, any>>
): Promise<void>Loads metadata from the Swagger TypeScript compiler plugin for enhanced automatic documentation generation.
Usage:
// In your main.ts, before creating the document
await SwaggerModule.loadPluginMetadata(() =>
import('./swagger-plugin-metadata')
);
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Set global prefix
app.setGlobalPrefix('api/v1');
// Load plugin metadata for enhanced documentation
if (process.env.NODE_ENV !== 'production') {
try {
await SwaggerModule.loadPluginMetadata(() =>
import('./swagger-plugin-metadata')
);
} catch (error) {
console.warn('Could not load Swagger plugin metadata:', error.message);
}
}
// Create comprehensive document configuration
const config = new DocumentBuilder()
.setTitle('E-Commerce Platform API')
.setDescription('Comprehensive e-commerce platform REST API')
.setVersion('2.0.0')
.setContact('API Team', 'https://example.com/contact', 'api@example.com')
.setLicense('MIT', 'https://opensource.org/licenses/MIT')
.addServer('https://api.example.com', 'Production')
.addServer('https://staging-api.example.com', 'Staging')
.addBearerAuth()
.addApiKey({ type: 'apiKey', in: 'header', name: 'X-API-Key' })
.addTag('Authentication', 'User authentication and authorization')
.addTag('Products', 'Product catalog management')
.addTag('Orders', 'Order processing and fulfillment')
.build();
// Generate document with advanced options
const document = SwaggerModule.createDocument(app, config, {
deepScanRoutes: true,
autoTagControllers: true,
operationIdFactory: (controllerKey, methodKey, version) =>
`${controllerKey}_${methodKey}${version ? `_v${version}` : ''}`,
});
// Setup with extensive customization
SwaggerModule.setup('docs', app, document, {
useGlobalPrefix: true,
explorer: true,
swaggerOptions: {
persistAuthorization: true,
displayRequestDuration: true,
filter: true,
showExtensions: true,
showCommonExtensions: true,
docExpansion: 'list'
},
customSiteTitle: 'E-Commerce API - Interactive Documentation',
customfavIcon: 'https://example.com/favicon.ico',
customCss: `
.swagger-ui .topbar { display: none; }
.swagger-ui .info .title { color: #1f2937; }
`,
customCssUrl: 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600',
});
await app.listen(3000);
console.log(`Application is running on: ${await app.getUrl()}`);
console.log(`Swagger UI available at: ${await app.getUrl()}/api/v1/docs`);
}
bootstrap();async function setupMultiVersionDocs(app: INestApplication) {
// V1 API Documentation
const v1Config = new DocumentBuilder()
.setTitle('API v1')
.setVersion('1.0')
.addBearerAuth()
.build();
const v1Document = SwaggerModule.createDocument(app, v1Config, {
include: [V1Module],
ignoreGlobalPrefix: true
});
SwaggerModule.setup('docs/v1', app, v1Document);
// V2 API Documentation
const v2Config = new DocumentBuilder()
.setTitle('API v2')
.setVersion('2.0')
.addBearerAuth()
.addOAuth2()
.build();
const v2Document = SwaggerModule.createDocument(app, v2Config, {
include: [V2Module],
ignoreGlobalPrefix: true
});
SwaggerModule.setup('docs/v2', app, v2Document);
// Combined documentation with version selector
const combinedConfig = new DocumentBuilder()
.setTitle('Multi-Version API')
.setVersion('latest')
.addBearerAuth()
.build();
const combinedDocument = SwaggerModule.createDocument(app, combinedConfig, {
include: [V1Module, V2Module],
deepScanRoutes: true
});
SwaggerModule.setup('docs', app, combinedDocument, {
explorer: true,
swaggerOptions: {
urls: [
{ url: '/docs/v1-json', name: 'API v1' },
{ url: '/docs/v2-json', name: 'API v2' }
]
}
});
}function setupSwaggerBasedOnEnvironment(app: INestApplication, document: OpenAPIObject) {
const isDevelopment = process.env.NODE_ENV === 'development';
const isStaging = process.env.NODE_ENV === 'staging';
const isProduction = process.env.NODE_ENV === 'production';
if (isDevelopment) {
// Full-featured documentation for development
SwaggerModule.setup('api-docs', app, document, {
explorer: true,
swaggerOptions: {
persistAuthorization: true,
displayRequestDuration: true,
tryItOutEnabled: true
},
customSiteTitle: 'Dev API Documentation'
});
} else if (isStaging) {
// Limited documentation for staging
SwaggerModule.setup('docs', app, document, {
explorer: false,
swaggerOptions: {
tryItOutEnabled: false
},
customSiteTitle: 'Staging API Documentation'
});
} else if (isProduction) {
// Minimal or no documentation for production
if (process.env.ENABLE_DOCS === 'true') {
SwaggerModule.setup('internal-docs', app, document, {
ui: true,
raw: ['json'], // Only JSON, no YAML
swaggerOptions: {
tryItOutEnabled: false,
supportedSubmitMethods: [] // Disable try-it-out
},
customSiteTitle: 'Internal API Documentation'
});
}
// Otherwise, no Swagger setup in production
}
}The SwaggerModule provides powerful, flexible integration between NestJS applications and Swagger UI, supporting everything from basic documentation needs to sophisticated multi-version, multi-environment setups with extensive customization options.