OpenApi for your REST APIs in NestJS

Setup Swagger to generate an OpenApi documentation for your REST endpoints.

Authors
Marc Stammerjohann Marc Stammerjohann
Published at

The OpenApi documentation is a useful API playground for you to test or to share with other developers and for client generation tools (e.g ng-openapi-gen for Angular).

You'll find the source code in this repo.

Setup Swagger

Start with installing the Swagger dependencies.

# express
npm install --save @nestjs/swagger swagger-ui-express class-transformer class-validator

# fastify
npm install --save @nestjs/swagger fastify-swagger class-transformer class-validator

Now setup the initialization of Swagger in your main.ts file.

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('NestJS Swagger')
    .setDescription('API description')
    .setVersion('1.0')
    .build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);

  await app.listen(3000);
}
bootstrap();

The setup is complete, start your Nest application npm run start:dev and visit the Swagger endpoint localhost:3000/api.

Swagger API after initial setup

Swagger API will be available at the path you provide in SwaggerModule.setup('api',...) at http://localhost:3000/api. Access the JSON file by opening http://localhost:3000/api-json for express and http://localhost:3000/api/json for fastify.

Generate in the next step CRUD endpoints for a resource like users or products and add type definitions for Swagger.

Generate REST resource

Use the Nest CLI to generate the boilerplate the resource for users.

nest generate resource
# short
nest g res

# CLI prompts
? What name would you like to use for this resource (plural, e.g., "users")? users
? What transport layer do you use? REST API
? Would you like to generate CRUD entry points? (Y/n) y

You'll find a new users directory under src containing all the boilerplates for your REST endpoints - module, controller, service, entity and dto files.

Start again the Nest application and you should see the new users endpoints in the Swagger API.

Users endpoints in the Swagger API

Api decorators

Apply available decorators prefixed with Api to expose the properties for .dto.ts and .entity.ts files and the responses for your CRUD endpoints.

Tags

Group your endpoints together by using @ApiTags(...tags) at the controller level.

import { Controller } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';

@Controller('users')
@ApiTags('users') // 👈 apply tags
export class UsersController {
  ...
}

Group endpoints with tags

Property

Let's add the following properties name, email, password to the CreateProductDto and mark name as optional.

export class CreateUserDto {
  email: string;
  password: string;
  name?: string | null;
}

To expose those properties to the Swagger API use @ApiProperty(options) at the property level and pass options like required, default, description and more.

import { ApiProperty } from '@nestjs/swagger';

export class CreateUserDto {
  @ApiProperty()
  email: string;
  @ApiProperty()
  password: string;
  @ApiProperty({ required: false, nullable: true })
  name?: string | null;
}

Refresh the Swagger API and you should see the properties for the CreateProductDto.

CreateProductDto properties with name optional

Also have a look at the UpdateProductDto schema in Swagger. The same properties are shown but all of them are marked as optional. This is because of PartialType also called Mapped types provided by Nest.

import { PartialType } from '@nestjs/swagger';
import { CreateUserDto } from './create-user.dto';

export class UpdateUserDto extends PartialType(CreateUserDto) {}

PartialType applies the same properties from CreateUserDto but set to optional.

Response

Add the same properties as before to the user.entity.ts and only expose name and email to Swagger.

import { ApiProperty } from '@nestjs/swagger';

export class User {
  @ApiProperty()
  email: string;
  password: string;
  @ApiProperty({ required: false, nullable: true })
  name?: string | null;
}

Additionally, Swagger needs help to pick up the response type. Annotate your REST endpoints with the custom @ApiResponse() specifying the status code and the response type or choose a short-hand API response (e.g. @ApiOkResponse(), @ApiCreatedResponse(), ...).

  • @ApiOkResponse: GET and DELETE
  • @ApiCreatedResponse: POST and PATCH
  • @ApiForbiddenResponse: endpoint might throw forbidden (403) exception
import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { User } from './entities/user.entity';

@Controller('users')
@ApiTags('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  @ApiCreatedResponse({ type: User })
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

  @Get()
  @ApiOkResponse({ type: [User] }) // 👈 array notation
  findAll() {
    return this.usersService.findAll();
  }

  @Get(':id')
  @ApiOkResponse({ type: User })
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(+id);
  }

  @Patch(':id')
  @ApiCreatedResponse({ type: User })
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(+id, updateUserDto);
  }

  @Delete(':id')
  @ApiOkResponse({ type: User })
  remove(@Param('id') id: string) {
    return this.usersService.remove(+id);
  }
}

When the response type is an array, you must indicate it using the array bracket notation ([ ]) around the type or set isArray to true. GET /users response is an array of User annotation looks like this:

@ApiOkResponse({ type: [User] })

@ApiOkResponse({ type: User, isArray: true })

You'll see the endpoints with the new response type of User.

CreateProductDto properties with name optional

Swagger CLI Plugin

Exposing the properties and responses to Swagger results in additional boilerplate. Nest commes with a Swagger CLI Plugin to reduce boilerplate in your .dto.ts and .entity.ts files. Enable the plugin in your nest-cli.json file.

{
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "plugins": ["@nestjs/swagger"]
  }
}

Before: User entity, CreateUserDto and UsersController with boilerplate.

export class User {
  @ApiProperty()
  email: string;
  password: string;
  @ApiProperty({ required: false, nullable: true })
  name?: string | null;
}
import { ApiProperty } from '@nestjs/swagger';

export class CreateUserDto {
  @ApiProperty()
  email: string;
  @ApiProperty()
  password: string;
  @ApiProperty({ required: false, nullable: true })
  name?: string | null;
}
import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { User } from './entities/user.entity';

@Controller('users')
@ApiTags('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  @ApiCreatedResponse({ type: User })
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

  @Get()
  @ApiOkResponse({ type: [User] })
  findAll() {
    return this.usersService.findAll();
  }

  @Get(':id')
  @ApiOkResponse({ type: User })
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(+id);
  }

  @Patch(':id')
  @ApiCreatedResponse({ type: User })
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(+id, updateUserDto);
  }

  @Delete(':id')
  @ApiOkResponse({ type: User })
  remove(@Param('id') id: string) {
    return this.usersService.remove(+id);
  }
}

After: CLI plugin enabled and without boilerplate. You need to add @ApiHideProperty otherwise the plugin will also expose the password property.

import { ApiHideProperty } from '@nestjs/swagger';

export class User {
  email: string;
  @ApiHideProperty()
  password: string;
  name?: string | null;
}
export class CreateUserDto {
  email: string;
  password: string;
  name?: string | null;
}
import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { ApiTags } from '@nestjs/swagger';
import { User } from './entities/user.entity';

@Controller('users')
@ApiTags('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  create(@Body() createUserDto: CreateUserDto): User {
    return this.usersService.create(createUserDto);
  }

  @Get()
  findAll(): User[] {
    return this.usersService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string): User {
    return this.usersService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto): User {
    return this.usersService.update(+id, updateUserDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string): User {
    return this.usersService.remove(+id);
  }
}

Advanced Swagger Types

Check out the following post for type-safe file uploads.

This allows you to directly test file upload in your Swagger documentation and is perfect for client generation tools to pick up the correct input values for file(s).

Table of Contents

Top of Page Sorry, could not load static page content Comments Related Articles

Related Posts

Find more posts like this one.

Authors
Marc Stammerjohann
August 26, 2021

NestJS: Type-safe File Uploads

Learn how to apply Swagger decorators for type-safe file upload endpoints.
NestJS Read More
Authors
Marc Stammerjohann
August 16, 2021

Send Emails with NestJS

Create Email Templates and send them with nodemailer from your Nest application
NestJS Read More
Authors
Marc Stammerjohann
August 17, 2021

Introducing NestJS Prisma Library and Schematics

Library and schematics to add Prisma integration to a NestJS application
NestJS Prisma Read More
Authors
Marc Stammerjohann
June 03, 2021

Dockerizing a NestJS app with Prisma and PostgreSQL

How to dockerize a NestJS application with Prisma and PostgreSQL.
NestJS Prisma Docker Read More
Authors
Marc Stammerjohann
April 07, 2020

GraphQL Code-First Approach with NestJS 7

Create a GraphQL API using Code-First Approach with NestJS 7.
NestJS GraphQL Prisma Read More
Authors
Marc Stammerjohann
April 07, 2020

Deploy NestJS with Prisma to Heroku

Deploy a NestJS application with Prisma 2.0 to Heroku and connect to a PostgreSQL database.
NestJS Prisma Heroku Read More
Authors
Marc Stammerjohann
June 17, 2021

How to query your database using Prisma with NestJS

Learn how to setup a database with Prisma 2.0 and query data using NestJS.
NestJS Prisma Read More

Sign up for our newsletter

Sign up for our newsletter to stay up to date. Sent every other week.

We care about the protection of your data. Read our Privacy Policy.