Documenting REST APIs with OpenAPI specs (NestJS/Swagger)
Tags: nestjs, swagger, api, documentation
Published: 2023-03-16 00:46:12

---
title: Documenting REST APIs with OpenAPI specs (NestJS/Swagger)
published: true
tags: ['nestjs', 'swagger', 'api', 'docs']
canonical_url: https://sevic.dev/notes/swagger-openapi-docs-nestjs/
---

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

```yaml
openapi: 3.0.0
paths:
  /users:
    post:
      operationId: UsersController_createUser
      summary: Create user
      description: Create a new user
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserDto'
      responses:
        '201':
          description: 'User is created'
info:
  title: nestjs-starter
  description: Minimal NestJS boilerplate
  version: 0.1.0
  contact: {}
tags: []
servers: []
components:
  securitySchemes:
    token:
      type: apiKey
      scheme: api_key
      in: header
      name: auth-token
  schemas:
    CreateUserDto:
      type: object
      properties:
        firstName:
          type: string
          example: tester
          description: first name of the user
      required:
        - 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.

```ts
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.

```json
{
  "compilerOptions": {
    "plugins": ["@nestjs/swagger"]
  }
}
```

JSON and YAML formats are generated at `/api-docs-json` and `/api-docs-yaml` endpoints, respectively.

### Decorators

- `ApiTags` groups endpoints

```ts
@ApiTags('users')
@Controller('users')
export class UsersController {
// ...
}
```

- `ApiOperation` provides more details like a summary and description of the endpoint

```ts
@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

```ts
@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.

```ts
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

```ts
@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 like `ApiBadRequestResponse`.

```ts
// ...
@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 like `ApiBearerAuth`.

```ts
@ApiSecurity('token')
@Controller()
export class AppController {
// ...
}
// ...
@ApiBearerAuth()
@Controller()
export class AppController {
// ...
}
```

- `ApiExcludeEndpoint` and `ApiExcludeController` exclude one endpoint and the whole controller, respectively.

```ts
export class AppController {
  @ApiExcludeEndpoint()
  @Get()
  getHello(): string {
    // ...
  }
}
// ...
@ApiExcludeController()
@Controller()
export class AppController {
  // ...
}
```

- `ApiBody` with `ApiExtraModels` add an example for the request body

```ts
  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 &rarr; 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](https://sevic.dev/nestjs-starter?ref=devto-swagger) to the boilerplate I use for the development. It contains the examples mentioned above with more details.