OpenAPI (Swagger)
The OpenAPI specification is a language-agnostic definition format used to describe RESTful APIs. Nest provides a dedicated module which allows generating such a specification by leveraging decorators.
Installation
To begin using it, we first install the required dependencies.
$ npm install --save @nestjs/swagger swagger-ui-express
If you use fastify, install fastify-swagger
instead of swagger-ui-express
:
$ npm install --save @nestjs/swagger fastify-swagger
Bootstrap
Once the installation process is complete, open the main.ts
file and initialize Swagger using the SwaggerModule
class:
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const options = new DocumentBuilder()
.setTitle('Cats example')
.setDescription('The cats API description')
.setVersion('1.0')
.addTag('cats')
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('api', app, document);
await app.listen(3000);
}
bootstrap();
The DocumentBuilder
helps to structure a base document that conforms to the OpenAPI Specification. It provides several methods that allow setting such properties as title, description, version, etc. In order to create a full document (with all HTTP routes defined) we use the createDocument()
method of the SwaggerModule
class. This method takes two arguments, an application instance and a Swagger options object.
Once we create a document, we can call setup()
method. It accepts:
- the path to mount the Swagger UI
- an application instance
- the document object instantiated above
Now you can run the following command to start the HTTP server:
$ npm run start
While the application is running, open your browser and navigate to http://localhost:3000/api
. You should see the Swagger UI.
The SwaggerModule
automatically reflects all of your endpoints. Also, in order to display the Swagger UI, @nestjs/swagger
makes use of either swagger-ui-express
or fastify-swagger
depending on the platform.
Hint To generate and download a Swagger JSON file, navigate to
http://localhost:3000/api-json
in your browser (assuming that your Swagger documentation is available underhttp://localhost:3000/api
).
Route parameters
The SwaggerModule
searches for all @Body()
, @Query()
, and @Param()
decorators in route handlers to generate the API document. It also creates corresponding model definitions by taking advantage of reflection. Consider the following code:
@Post()
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
Hint To explicitly set the body definition use the
@ApiBody()
decorator (@nestjs/swagger
package).
Based on the CreateCatDto
, the module definition will be created:
As you can see, the definition is empty although the class has a few declared properties. In order to make the class properties visible to the SwaggerModule
, we have to either annotate them with the @ApiProperty()
decorator or use a CLI plugin (read more in the Plugin section) which will do it automatically:
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}
Hint Consider using the Swagger plugin (see Plugin section) which will automatically do it for you.
Let’s open the browser and verify the generated CreateCatDto
model:
In addition, the @ApiProperty()
decorator allows setting various Schema Object properties:
@ApiProperty({
description: 'The age of a cat',
minimum: 1,
default: 1,
})
age: number;
Hint Instead of explicitly typing the
@ApiProperty({ required: false })
you can use@ApiPropertyOptional()
short-hand decorator.
In order to explicitly set the type of the property, use the type
key:
@ApiProperty({
type: Number,
})
age: number;
Arrays
When the property is an array, we must manually indicate the array type as shown below:
@ApiProperty({ type: [String] })
names: string[];
Hint Consider using the Swagger plugin (see Plugin section) which will automatically detect arrays.
Either include the type as the first element of an array (as shown above) or set the isArray
property to true
.
Official enterprise support
- Providing technical guidance
- Performing in-depth code reviews
- Mentoring team members
- Advising best practices
Circular dependencies
When you have circular dependencies between classes, use a lazy function to provide the SwaggerModule
with type information:
@ApiProperty({ type: () => Node })
node: Node;
Hint Consider using the Swagger plugin (see Plugin section) which will automatically detect circular dependencies.
Generics and interfaces
Since TypeScript does not store metadata about generics or interfaces, when you use them in your DTOs, SwaggerModule
may not be able to properly generate model definitions at runtime. For instance, the following code won’t be correctly inspected by the Swagger module:
createBulk(@Body() usersDto: CreateUserDto[])
In order to overcome this limitation, you can set the type explicitly:
@ApiBody({ type: [CreateUserDto] })
createBulk(@Body() usersDto: CreateUserDto[])
Mapped types
As you build out features like CRUD (Create/Read/Update/Delete) it’s often useful to construct variants on a base entity type. Nest provides several utility functions that perform type transformations to make this task more convenient.
When building input validation types (also called DTOs), it’s often useful to build create and update variations on the same type. For example, the create variant may require all fields, while the update variant may make all fields optional.
Nest provides the PartialType()
utility function to make this task easier and minimize boilerplate.
The PartialType()
function returns a type (class) with all the properties of the input type set to optional. For example, suppose we have a create type as follows:
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}
By default, all of these fields are required. To create a type with the same fields, but with each one optional, use PartialType()
passing the class reference (CreateCatDto
) as an argument:
export class UpdateCatDto extends PartialType(CreateCatDto) {}
Hint The
PartialType()
function is imported from the@nestjs/swagger
package.
The PickType()
function constructs a new type (class) by picking a set of properties from an input type. For example, suppose we start with a type like:
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}
We can pick a set of properties from this class using the PickType()
utility function:
export class UpdateCatAgeDto extends PickType(CreateCatDto, ['age'] as const) {}
Hint The
PickType()
function is imported from the@nestjs/swagger
package.
The OmitType()
function constructs a type by picking all properties from an input type and then removing a particular set of keys. For example, suppose we start with a type like:
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}
We can generate a derived type that has every property exceptname
as shown below. In this construct, the second argument to OmitType
is an array of property names.
export class UpdateCatDto extends OmitType(CreateCatDto, ['name'] as const) {}
Hint The
OmitType()
function is imported from the@nestjs/swagger
package.
The IntersectionType()
function combines two types into one new type (class). For example, suppose we start with two types like:
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
breed: string;
}
export class AdditionalCatInfo {
@ApiProperty()
color: string;
}
We can generate a new type that combines all properties in both types.
export class UpdateCatDto extends IntersectionType(CreateCatDto, AdditionalCatInfo) {}
Hint The
IntersectionType()
function is imported from the@nestjs/swagger
package.
The type mapping utility functions are composable. For example, the following will produce a type (class) that has all of the properties of the CreateCatDto
type except for name
, and those properties will be set to optional:
export class UpdateCatDto extends PartialType(
OmitType(CreateCatDto, ['name'] as const),
) {}
Enums
To identify an enum
, we must manually set the enum
property on the @ApiProperty
with an array of values.
@ApiProperty({ enum: ['Admin', 'Moderator', 'User']})
role: UserRole;
Alternatively, define an actual TypeScript enum as follows:
export enum UserRole {
Admin = 'Admin',
Moderator = 'Moderator',
User = 'User',
}
You can then use the enum directly with the @Query()
parameter decorator in combination with the @ApiQuery()
decorator.
@ApiQuery({ name: 'role', enum: UserRole })
async filterByRole(@Query('role') role: UserRole = UserRole.User) {}
With isArray
set to true, the enum
can be selected as a multi-select:
Enums schema
By default, the enum
property will add a raw definition of Enum on the parameter
.
- breed:
type: 'string'
enum:
- Persian
- Tabby
- Siamese
The above specification works fine for most cases. However, if you are utilizing a tool that takes the specification as input and generates client-side code, you might run into a problem with the generated code containing duplicated enums
. Consider the following code snippet:
// generated client-side code
export class CatDetail {
breed: CatDetailEnum;
}
export class CatInformation {
breed: CatInformationEnum;
}
export enum CatDetailEnum {
Persian = 'Persian',
Tabby = 'Tabby',
Siamese = 'Siamese',
}
export enum CatInformationEnum {
Persian = 'Persian',
Tabby = 'Tabby',
Siamese = 'Siamese',
}
Hint The above snippet is generated using a tool called NSwag.
You can see that now you have two enums
that are exactly the same. To address this issue, you can pass an enumName
next to enum
property in your decorator.
export class CatDetail {
@ApiProperty({ enum: CatBreed, enumName: 'CatBreed' })
breed: CatBreed;
}
The enumName
property enables @nestjs/swagger
to turn CatBreed
into its own schema
which in turns makes CatBreed
enum reusable. The specification will look like the following:
CatDetail:
type: 'object'
properties:
...
- breed:
schema:
$ref: '#/components/schemas/CatBreed'
CatBreed:
type: string
enum:
- Persian
- Tabby
- Siamese
Hint Any decorator that takes
enum
as a property will also takeenumName
.
Raw definitions
In some specific scenarios (e.g. deeply nested arrays, matrices), you may want to describe your type by hand.
@ApiProperty({
type: 'array',
items: {
type: 'array',
items: {
type: 'number',
},
},
})
coords: number[][];
Likewise, in order to define your input/output content manually in controller classes, use the schema
property:
@ApiBody({
schema: {
type: 'array',
items: {
type: 'array',
items: {
type: 'number',
},
},
},
})
async create(@Body() coords: number[][]) {}
Extra models
In order to define additional models that should be inspected by Swagger module, use the @ApiExtraModels()
decorator:
@ApiExtraModels(ExtraModel)
export class CreateCatDto {}
Then, you can get the reference ($ref
) to your model using getSchemaPath(ExtraModel)
:
'application/vnd.api+json': {
schema: { $ref: getSchemaPath(ExtraModel) },
},
oneOf, anyOf, allOf
In order to combine schemas, you can use oneOf
, anyOf
or allOf
keywords (read more).
@ApiProperty({
oneOf: [
{ $ref: getSchemaPath(Cat) },
{ $ref: getSchemaPath(Dog) },
],
})
pet: Cat | Dog;
If you want to define a polymorphic array (i.e., an array whose members span multiple schemas), you should use a raw definition (see above) to define your type by hand.
type Pet = Cat | Dog;
@ApiProperty({
type: 'array',
items: {
oneOf: [
{ $ref: getSchemaPath(Cat) },
{ $ref: getSchemaPath(Dog) },
],
},
})
pets: Pet[];
Hint
getSchemaPath()
function is imported from@nestjs/swagger
.
Both Cat
and Dog
must be defined as extra models using the @ApiExtraModels()
decorator (at the class-level).
Multiple specifications
The SwaggerModule
provides a way to support multiple specifications. In other words, you can serve different documentation, with different UIs, on different endpoints.
To support multiple specifications, your application must be written with a modular approach. The createDocument()
method takes in a 3rd argument, extraOptions
, which is an object with a the property include
. The include
property has a value which is an array of modules.
You can setup multiple specifications support as shown below:
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
/**
* createDocument(application, configurationOptions, extraOptions);
*
* createDocument method takes in an optional 3rd argument "extraOptions"
* which is an object with "include" property where you can pass an Array
* of Modules that you want to include in that Swagger Specification
* E.g: CatsModule and DogsModule will have two separate Swagger Specifications which
* will be exposed on two different SwaggerUI with two different endpoints.
*/
const options = new DocumentBuilder()
.setTitle('Cats example')
.setDescription('The cats API description')
.setVersion('1.0')
.addTag('cats')
.build();
const catDocument = SwaggerModule.createDocument(app, options, {
include: [CatsModule],
});
SwaggerModule.setup('api/cats', app, catDocument);
const secondOptions = new DocumentBuilder()
.setTitle('Dogs example')
.setDescription('The dogs API description')
.setVersion('1.0')
.addTag('dogs')
.build();
const dogDocument = SwaggerModule.createDocument(app, secondOptions, {
include: [DogsModule],
});
SwaggerModule.setup('api/dogs', app, dogDocument);
await app.listen(3000);
}
bootstrap();
Now you can start your server with the following command:
$ npm run start
Navigate to http://localhost:3000/api/cats
to see the Swagger UI for cats:
In turn, http://localhost:3000/api/dogs
will expose the Swagger UI for dogs:
Tags
To attach a controller to a specific tag, use the @ApiTags(...tags)
decorator.
@ApiTags('cats')
@Controller('cats')
export class CatsController {}
Headers
To define custom headers that are expected as part of the request, use @ApiHeader()
.
@ApiHeader({
name: 'X-MyHeader',
description: 'Custom header',
})
@Controller('cats')
export class CatsController {}
Responses
To define a custom HTTP response, we use @ApiResponse()
decorator.
@Post()
@ApiResponse({ status: 201, description: 'The record has been successfully created.'})
@ApiResponse({ status: 403, description: 'Forbidden.'})
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
Nest provides a set of short-hand API response decorators that inherit from the @ApiResponse
decorator:
@ApiOkResponse()
@ApiCreatedResponse()
@ApiAcceptedResponse()
@ApiNoContentResponse()
@ApiMovedPermanentlyResponse()
@ApiBadRequestResponse()
@ApiUnauthorizedResponse()
@ApiNotFoundResponse()
@ApiForbiddenResponse()
@ApiMethodNotAllowedResponse()
@ApiNotAcceptableResponse()
@ApiRequestTimeoutResponse()
@ApiConflictResponse()
@ApiTooManyRequestsResponse()
@ApiGoneResponse()
@ApiPayloadTooLargeResponse()
@ApiUnsupportedMediaTypeResponse()
@ApiUnprocessableEntityResponse()
@ApiInternalServerErrorResponse()
@ApiNotImplementedResponse()
@ApiBadGatewayResponse()
@ApiServiceUnavailableResponse()
@ApiGatewayTimeoutResponse()
@ApiDefaultResponse()
@Post()
@ApiCreatedResponse({ description: 'The record has been successfully created.'})
@ApiForbiddenResponse({ description: 'Forbidden.'})
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
To specify a return model for a request, we must create a class and annotate all properties with the @ApiProperty()
decorator.
export class Cat {
@ApiProperty()
id: number;
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}
Then, Cat
model must be used in combination with the type
property of the response decorator.
@ApiTags('cats')
@Controller('cats')
export class CatsController {
@Post()
@ApiCreatedResponse({
description: 'The record has been successfully created.',
type: Cat,
})
async create(@Body() createCatDto: CreateCatDto): Promise<Cat> {
return this.catsService.create(createCatDto);
}
}
Let’s open the browser and verify the generated Cat
model:
Global prefix
To ignore a global prefix for routes set through setGlobalPrefix()
, use ignoreGlobalPrefix
:
const document = SwaggerModule.createDocument(app, options, {
ignoreGlobalPrefix: true,
});
Security
To define which security mechanisms should be used for a specific operation, use the @ApiSecurity()
decorator.
@ApiSecurity('basic')
@Controller('cats')
export class CatsController {}
Before you run your application, remember to add the security definition to your base document using DocumentBuilder
:
const options = new DocumentBuilder().addSecurity('basic', {
type: 'http',
scheme: 'basic',
});
Some of the most popular authentication techniques are predefined (e.g. basic
and bearer
) and therefore you don’t have to define security mechanisms manually as shown above.
Learn the right way!
- 60+ chapters
- 4+ hours of videos
- Official certificate
- Deep-dive sessions
Basic authentication
To enable basic authentication, use @ApiBasicAuth()
.
@ApiBasicAuth()
@Controller('cats')
export class CatsController {}
Before you run your application, remember to add the security definition to your base document using DocumentBuilder
:
const options = new DocumentBuilder().addBasicAuth();
Bearer authentication
To enable bearer authentication, use @ApiBearerAuth()
.
@ApiBearerAuth()
@Controller('cats')
export class CatsController {}
Before you run your application, remember to add the security definition to your base document using DocumentBuilder
:
const options = new DocumentBuilder().addBearerAuth();
OAuth2 authentication
To enable OAuth2, use @ApiOAuth2()
.
@ApiOAuth2(['pets:write'])
@Controller('cats')
export class CatsController {}
Before you run your application, remember to add the security definition to your base document using DocumentBuilder
:
const options = new DocumentBuilder().addOAuth2();
Cookie authentication
To enable cookie authentication, use @ApiCookieAuth()
.
@ApiCookieAuth()
@Controller('cats')
export class CatsController {}
Before you run your application, remember to add the security definition to your base document using DocumentBuilder
:
const options = new DocumentBuilder().addCookieAuth('optional-session-id');
File upload
You can enable file upload for a specific method with the @ApiBody
decorator together with @ApiConsumes()
. Here’s a full example using the File Upload technique:
@UseInterceptors(FileInterceptor('file'))
@ApiConsumes('multipart/form-data')
@ApiBody({
description: 'List of cats',
type: FileUploadDto,
})
uploadFile(@UploadedFile() file) {}
Where FileUploadDto
is defined as follows:
class FileUploadDto {
@ApiProperty({ type: 'string', format: 'binary' })
file: any;
}
Extensions
To add an Extension to a request use the @ApiExtension()
decorator. The extension name must be prefixed with x-
.
@ApiExtension('x-foo', { hello: 'world' })
Decorators
All of the available OpenAPI decorators have an Api
prefix to distinguish them from the core decorators. Below is a full list of the exported decorators along with a designation of the level at which the decorator may be applied.
@ApiOperation() | Method |
@ApiResponse() | Method / Controller |
@ApiProduces() | Method / Controller |
@ApiConsumes() | Method / Controller |
@ApiBearerAuth() | Method / Controller |
@ApiOAuth2() | Method / Controller |
@ApiBasicAuth() | Method / Controller |
@ApiSecurity() | Method / Controller |
@ApiExtraModels() | Method / Controller |
@ApiBody() | Method |
@ApiParam() | Method |
@ApiQuery() | Method |
@ApiHeader() | Method / Controller |
@ApiExcludeEndpoint() | Method |
@ApiTags() | Method / Controller |
@ApiProperty() | Model |
@ApiPropertyOptional() | Model |
@ApiHideProperty() | Model |
@ApiExtension() | Method |
Plugin
TypeScript’s metadata reflection system has several limitations which make it impossible to, for instance, determine what properties a class consists of or recognize whether a given property is optional or required. However, some of these constraints can be addressed at compilation time. Nest provides a plugin that enhances the TypeScript compilation process to reduce the amount of boilerplate code required.
Hint This plugin is opt-in. If you prefer, you can declare all decorators manually, or only specific decorators where you need them.
The Swagger plugin will automatically:
- annotate all DTO properties with
@ApiProperty
unless@ApiHideProperty
is used - set the
required
property depending on the question mark (e.g.name?: string
will setrequired: false
) - set the
type
orenum
property depending on the type (supports arrays as well) - set the
default
property based on the assigned default value - set several validation rules based on
class-validator
decorators (ifclassValidatorShim
set totrue
) - add a response decorator to every endpoint with a proper status and
type
(response model)
Please, note that your filenames must have one of the following suffixes: ['.dto.ts', '.entity.ts']
(e.g., create-user.dto.ts
) in order to be analysed by the plugin.
Previously, if you wanted to provide an interactive experience with the Swagger UI, you had to duplicate a lot of code to let the package knows how your models/components should be declared in the specification. For example, you could define a simple CreateUserDto
class as follows:
export class CreateUserDto {
@ApiProperty()
email: string;
@ApiProperty()
password: string;
@ApiProperty({ enum: RoleEnum, default: [], isArray: true })
roles: RoleEnum[] = [];
@ApiProperty({ required: false, default: true })
isEnabled?: boolean = true;
}
While it’s not a big deal with medium-sized projects, it becomes pretty verbose & clunky once you have a large set of classes.
Now, with the Swagger plugin enabled, the above class definition can be declared simply:
export class CreateUserDto {
email: string;
password: string;
roles: RoleEnum[] = [];
isEnabled?: boolean = true;
}
The plugin adds appropriate decorators on the fly based on the Abstract Syntax Tree. Hence, you no longer have to struggle with @ApiProperty
decorators scattered throughout the entire project.
Hint The plugin will automatically generate any missing swagger properties, but if you need to override them, you simply set them explicitly via
@ApiProperty()
.
In order to enable the plugin, simply open nest-cli.json
(if you use Nest CLI) and add the following plugins
configuration:
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"plugins": ["@nestjs/swagger/plugin"]
}
}
You can use the options
property to customize the behavior of the plugin.
"plugins": [
{
"name": "@nestjs/swagger/plugin",
"options": {
"classValidatorShim": false
}
}
]
The options
property has to fulfill the following interface:
export interface PluginOptions {
dtoFileNameSuffix?: string[];
controllerFileNameSuffix?: string[];
classValidatorShim?: boolean;
}
Option | Default | Description |
---|---|---|
dtoFileNameSuffix | [‘.dto.ts’, ‘.entity.ts’] | DTO (Data Transfer Object) files suffix |
controllerFileNameSuffix | .controller.ts | Controller files suffix |
classValidatorShim | true | If set to true, the module will reuse class-validator validation decorators (e.g. @Max(10) will add max: 10 to schema definition) |
If you don’t use the CLI but instead have a custom webpack
configuration, you can use this plugin in combination with ts-loader
:
getCustomTransformers: (program: any) => ({
before: [require('@nestjs/swagger/plugin').before({}, program)]
}),
Migration to 4.0
If you’re currently using @nestjs/swagger@3.*
, note the following breaking/API changes in version 4.0.
The following decorators have been changed/renamed:
@ApiModelProperty
is now@ApiProperty
@ApiModelPropertyOptional
is now@ApiPropertyOptional
@ApiResponseModelProperty
is now@ApiResponseProperty
@ApiImplicitQuery
is now@ApiQuery
@ApiImplicitParam
is now@ApiParam
@ApiImplicitBody
is now@ApiBody
@ApiImplicitHeader
is now@ApiHeader
@ApiOperation({ title: 'test' })
is now@ApiOperation({ summary: 'test' })
@ApiUseTags
is now@ApiTags
DocumentBuilder
breaking changes (updated method signatures):
addTag
addBearerAuth
addOAuth2
setContactEmail
is nowsetContact
setHost
has been removedsetSchemes
has been removed (use theaddServer
instead, e.g.,addServer('http://')
)
The following methods have been added:
addServer
addApiKey
addBasicAuth
addSecurity
addSecurityRequirements
Hoodies, T-shirts, and accessories!
Support our future development by shopping in the official store!
Example
A working example is available here.