Documenting REST APIs with OpenAPI specs (NestJS/Swagger)
OpenAPI is a language-agnostic specification for declaring API documentation for REST APIs. It contains the following information:
- API information like title, description, version
- endpoints definitions with request and response parameters
- DTOs and security schemas
openapi: 3.0.0paths:/users:post:operationId: UsersController_createUsersummary: Create userdescription: Create a new userparameters: []requestBody:required: truecontent:application/json:schema:$ref: '#/components/schemas/CreateUserDto'responses:'201':description: 'User is created'info:title: nestjs-starterdescription: Minimal NestJS boilerplateversion: 0.1.0contact: {}tags: []servers: []components:securitySchemes:token:type: apiKeyscheme: api_keyin: headername: auth-tokenschemas:CreateUserDto:type: objectproperties:firstName:type: stringexample: testerdescription: first name of the userrequired:- firstName
NestJS provides a Swagger plugin for generating the API docs.
Setup
Configure API documentation with the specified endpoint, like /api-docs
, which shows the generated docs.
const SWAGGER_API_ENDPOINT = '/api-docs';// ...export const setupApiDocs = (app: INestApplication): void => {const options = new DocumentBuilder().setTitle(SWAGGER_API_TITLE).setDescription(SWAGGER_API_DESCRIPTION).setVersion(SWAGGER_API_VERSION).addSecurity('token', {type: 'apiKey',scheme: 'api_key',in: 'header',name: 'auth-token'}).addBearerAuth().build();const document = SwaggerModule.createDocument(app, options);SwaggerModule.setup(SWAGGER_API_ENDPOINT, app, document);};
Configure the plugin in the NestJS config file.
{"compilerOptions": {"plugins": ["@nestjs/swagger"]}}
JSON and YAML formats are generated at /api-docs-json
and /api-docs-yaml
endpoints, respectively.
Decorators
ApiTags
groups endpoints
@ApiTags('users')@Controller('users')export class UsersController {// ...}
ApiOperation
provides more details like a summary and description of the endpoint
@ApiOperation({summary: 'Get user',description: 'Get user by id',})@Get(':id')async getById(@Param('id', new ParseUUIDPipe()) id: string,): Promise<UserDto> {// ...}
ApiOperation
can be used to mark an endpoint as deprecated
@ApiOperation({ deprecated: true })
@ApiProperty
and@ApiPropertyOptional
should be used for request and response DTOs fields. Example and description values will be shown in the generated documentation.
export class CreateUserDto {@ApiProperty({ example: 'John', description: 'first name of the user' })// ...public firstName: string;@ApiPropertyOptional({ example: 'Doe', description: 'last name of the user' })// ...public lastName?: string;}
ApiHeader
documents endpoint headers
@ApiHeader({name: 'correlation-id',required: false,description: 'unique id for correlated logs',example: '7ea2c7f7-8b46-475d-86f8-7aaaa9e4a35b',})@Get()getHello(): string {// ...}
ApiResponse
specifies which responses are expected, like error responses. NestJS' Swagger package provides decorators for specific status codes likeApiBadRequestResponse
.
// ...@ApiResponse({ type: NotFoundException, status: HttpStatus.NOT_FOUND })@ApiBadRequestResponse({ type: BadRequestException })@Get(':id')async getById(@Param('id', new ParseUUIDPipe()) id: string,): Promise<UserDto> {return this.userService.findById(id);}// ...
ApiSecurity('token')
uses a custom-defined security strategy,token
in this case. Other options are to use already defined strategies likeApiBearerAuth
.
@ApiSecurity('token')@Controller()export class AppController {// ...}// ...@ApiBearerAuth()@Controller()export class AppController {// ...}
ApiExcludeEndpoint
andApiExcludeController
exclude one endpoint and the whole controller, respectively.
export class AppController {@ApiExcludeEndpoint()@Get()getHello(): string {// ...}}// ...@ApiExcludeController()@Controller()export class AppController {// ...}
ApiBody
withApiExtraModels
add an example for the request body
const CreateUserDtoExample = {firstName: 'Tester',};@ApiExtraModels(CreateUserDto)@ApiBody({schema: {oneOf: refs(CreateUserDto),example: CreateUserDtoExample,},})@Post()async createUser(@Body() newUser: CreateUserDto): Promise<UserDto> {// ...}
Importing API to Postman
Import JSON version of API docs as Postman API with Import → Link option (e.g., URL http://localhost:8081/api-docs-json
). Imported API collection will be available in the APIs tab.
Boilerplate
Here is the link to the boilerplate I use for the development. It contains the examples mentioned above with more details.