DEV Community

Anton Bahurinsky
Anton Bahurinsky

Posted on

5 tips for better Swagger docs in NestJS

Swagger is a powerful tool for documenting RESTful APIs when working with NestJS. And the NestJS developers have done great job for our comfortable work with Swagger within the framework. Now it is our responsibility as developers to apply this tool at its full potential and thus to make another bit of contribution of making this world a better place.

1. Document all endpoint response codes

When you develop a RESTful API (for example, on NestJS), there usually is a consumer of it: a frontend. The frontend is often developed by other people (than you/backend) who may not have access to the backend and/or enough backend skills. Thus, an API description is the simplest way to know what they are working with (against).

Frontend developers often have to handle different cases like invalid input, failed authentication, taken email address etc. And response codes are the way for them to know what actually happened on the backend.

Take some time to add decorators like @ApiCreatedResponse(), @ApiBadRequestResponse() or @ApiConflictResponse() to your endpoints. Some of them (like @ApiInternalServerErrorResponse()) don't even have to be added to every endpoint, but simply to a controller.

Additional descriptions may also me helpful:

// auth.controller.ts
@ApiConflictResponse({
  description: 'When the username is already taken.',
})
@Post('signup')
async signup() {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Response codes with description

2. Describe request and response return types

Just like with response codes, API consumers have to know what an endpoint consumes and returns. It is relatively easy to have those types in sync with actual code (see how SignupDto and LoggedInDto are used here):

// auth.controller.ts
@ApiBody({
  type: SignupDto,
})
@ApiCreatedResponse({ type: LoggedInDto })
@Post('signup')
async signup(@Body() signupDto: SignupDto): Promise<LoggedInDto> {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Request and response return types

3. Provide examples of field values

Remember, Swagger is not only declarative, it is interactive. This means that users are not only able to read the API docs, but also to make requests.

Defining field value examples will not only tell API consumers about what to expect. If a field value example is defined for an input entity or DTO, it becomes a default value for making requests:

// login.dto.ts
export class LoginDto {
  @ApiProperty({
    example: 'alice',
  })
  @Allow()
  username: string;

  @ApiProperty({
    example: 'Alice1111$',
  })
  @Allow()
  password: string;
}
Enter fullscreen mode Exit fullscreen mode

Field value examples

Field value examples as defaults

4. Enable user authentication

When applicable, of course.

One of the most popular authentication mechanisms for RESTful APIs are bearer access tokens. These tokens are often JWT, but their format doesn't affect our example.

In order to enable bearer token authentication you will first need to enable it in your main.ts, where you are defining and applying Swagger:

// main.ts -> bootstrap()
const config = new DocumentBuilder()
  // ...
  .addBearerAuth({
    type: 'http',
    scheme: 'bearer',
    in: 'header',
  })
  // ...
  .build();
Enter fullscreen mode Exit fullscreen mode

And then apply the @ApiBearerAuth() for an auth-restricted endpoint (or for entire controller if all its endpoints are restricted):

// auth.controller.ts
@Get('me')
@ApiBearerAuth()
async me() {
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Triggering authentication

Authenticating with a token

5. Manage tags

While being optional, tags are a great way of ordering the API endpoint groups. With tags, you can place the most used endpoints of the API to the top, which will increase the Swagger API efficiency for the entire team. In our example, database seeding and authentication endpoints are the most frequently used. Here is how we place them at the top:

// auth.controller.ts
@ApiTags('auth')
@Controller('auth')
export class AuthController {
  // ...
}
Enter fullscreen mode Exit fullscreen mode
// main.ts -> bootstrap()
const config = new DocumentBuilder()
  // ...
  .addTag('seed')
  .addTag('auth')
  // ... rest of tags
  .build();
Enter fullscreen mode Exit fullscreen mode

Top tags

Conclusion

That's it for now! Hope these tips will make you a good service.

If you want to see how everything from this post is implemented within an actual NestJS application, check out this repository.

If you have more ideas of how to improve Swagger API documenting, don't hesitate to leave a comment!

Don't stop coding, don't stop growing, and choose the good side 🇺🇦

Top comments (0)