Install swagger in NestJs

Hafez Heybati Goujani
5 min readJan 3, 2024

--

What Is OpenAPI?

OpenAPI Specification (formerly Swagger Specification) is an API description format for REST APIs. An OpenAPI file allows you to describe your entire API, including:

  • Available endpoints (/users) and operations on each endpoint (GET /users, POST /users)
  • Operation parameters Input and output for each operation
  • Authentication methods
  • Contact information, license, terms of use and other information.

API specifications can be written in YAML or JSON. The format is easy to learn and readable to both humans and machines. The complete OpenAPI Specification can be found on GitHub: OpenAPI 3.0 Specification

What Is Swagger?

Swagger is a set of open-source tools built around the OpenAPI Specification that can help you design, build, document and consume REST APIs. The major Swagger tools include:

  • Swagger Editor — browser-based editor where you can write OpenAPI definitions.
  • Swagger UI — renders OpenAPI definitions as interactive documentation.
  • Swagger Codegen — generates server stubs and client libraries from an OpenAPI definition.
  • Swagger Editor Next (beta) — browser-based editor where you can write and review OpenAPI and AsyncAPI definitions.
  • Swagger Core — Java-related libraries for creating, consuming, and working with OpenAPI definitions.
  • Swagger Parser — standalone library for parsing OpenAPI definitions
  • Swagger APIDom — provides a single, unifying structure for describing APIs across various description languages and serialization formats.

Refer to the https://swagger.io website for more information. Now lets go to install swagger in nestJs.

Install Swagger in NestJs:

1. Installing Required Packages:

Start by installing the necessary libraries:

npm install @nestjs/swagger swagger-ui-express

2. Setting Up Swagger:

Navigate to your main file (typically main.ts) and set up Swagger:

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('API Title')
.setDescription('API description')
.setVersion('1.0')
.addServer('http://localhost:3000/', 'Local environment')
.addServer('http://staging URL/', 'Staging')
.addServer('http://production URL/', 'Production')
.addTag('API Tag')
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('api-docs', app, document);
await app.listen(3000);
}
bootstrap();

With this setup, your Swagger UI will be available at http://localhost:3000/api-docs.

UI Swagger Documentation : http://localhost:3000/api-docs

It feels almost otherworldly, doesn’t it? The tedious task of manually drafting .yml or .json files has been whisked away. Everything is generated on the fly. And the best part? Instead of just hosting, you have the option to download and utilize it in diverse manners. Also, you can check we have a dropdown where we select the Server base URL.

3. Annotating APIs with Swagger Decorators:

NestJS provides decorators that integrate seamlessly with Swagger to auto-generate documentation.

a) Controllers and Routes:

Decorate your controllers and routes with appropriate Swagger decorators:

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


@ApiTags('User')
@Controller('user')
export class UserController {
@Get()
findAllUsers(): string {
return this.userService.findAll();
}
}

b) Defining Data Models:

Use class decorators to define data models and DTOs:

import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsNotEmpty, MinLength } from 'class-validator';


export enum UserRole {
Admin = 'Admin',
Moderator = 'Moderator',
User = 'User'
}
export class CreateUserDto {
id: number;
@ApiProperty({
example: 'rehmat.sayani@gmail.com',
required: true
})
@IsEmail()
email: string;
@ApiProperty({
example: '1234578910',
required: true
})
@IsNotEmpty()
@MinLength(10)
password: string;
@ApiProperty({ example: "Admin|Moderator|User"})
role: UserRole;
}

populated UI Schema for swagger

c) Further Descriptions:

Enhance endpoint documentation using @ApiResponse(), @ApiBody(), and more.

export class UserController {
@Post()
@ApiResponse({ status: 201, description: 'The record has been successfully created.'})
@ApiResponse({ status: 403, description: 'Forbidden.'})
@ApiBody({
type: CreateUserDto,
description: 'Json structure for user object',
})
async create(@Body() createUserDto: CreateUserDto) {
this.userService.create(createUserDto);
}
}

Poulates request body for swagger in the below image

JSON object structure for post new user

4. Adding Global Parameters:

In some scenarios, you may have parameters that are consistent across all routes, such as headers for authentication. In my case, we have a country parameter which is a query parameter and is required in all routes of the application. NestJS Swagger has a direct way to globally set these.

Here’s how:

Navigate to your main file again (typically main.ts) and update your code.

async function bootstrap() {
const app = await NestFactory.create(AppModule);
const options = new DocumentBuilder()
.setTitle('Your API Title')
.setDescription('Your API description')
.setVersion('1.0')
.addServer('http://localhost:3000/', 'Local environment')
.addServer('https://staging.yourapi.com/', 'Staging')
.addServer('https://production.yourapi.com/', 'Production')
.addTag('Your API Tag')
.addGlobalParameters({
name: 'country',
in: 'query'
})
.build();
const document = SwaggerModule.createDocument(app, options);
SwaggerModule.setup('api-docs', app, document);
await app.listen(process.env.PORT || 3000);
}
bootstrap();

Password protection

So first let’s protect the Swagger UI with HTTP basic auth requiring visitors to enter a username and password to access /docs or /docs-json. This can easily be done by implementing express-basic-auth, a simple plug & play HTTP basic auth middleware for Express.

npm i express-basic-auth

After installing express-basic-auth you would want to enable this middleware for your /docs and /docs-json endpoints. To do so modify main.ts like the following:

// main.ts
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import * as basicAuth from 'express-basic-auth';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(['/docs', '/docs-json'], basicAuth({
challenge: true,
users: {
[process.env.SWAGGER_USER]: process.env.SWAGGER_PASSWORD,
},
}));
const config = new DocumentBuilder()
.setTitle('API Docs')
.setDescription('The API documentation')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, document);
await app.listen(3000);
}
bootstrap();

The key to getting this to work is the right order, it is important to apply the middleware app.use(['/docs', '/docs-json'], basicAuth({…}) before you initialize Swagger.

basicAuth() in this scenario expects an object of users, I am using just one here. Keep in mind that it is always a good idea to not hardcode credentials, so relying on environment variables is a good option here. There are quite a few config options to express-basic-auth available, just check out the docs.

Hide Swagger UI on production

As mentioned the second thing I tend to do is hiding Swagger UI entirely on production. The most simple way to do so might be by wrapping a conditional statement around the parts initializing Swagger. Check this out:

// main.ts

import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import * as basicAuth from 'express-basic-auth';
import { AppModule } from './app.module';
const SWAGGER_ENVS = ['local', 'dev', 'staging'];
async function bootstrap() {
const app = await NestFactory.create(AppModule);
if (SWAGGER_ENVS.includes(process.env.NODE_ENV)) {
app.use(['/docs', '/docs-json'], basicAuth({
challenge: true,
users: {
[process.env.SWAGGER_USER]: process.env.SWAGGER_PASSWORD,
},
}));
const config = new DocumentBuilder()
.setTitle('API Docs')
.setDescription('The API documentation')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, document);
}
await app.listen(3000);
}
bootstrap();

This basically only applys the basic auth middleware and initializes Swagger when NODE_ENV is local, dev or staging. Depending on how you handle your environment names or have any other mechanism to check for a prodcution deployment, this may look slightly different in your project, but I think you get the gist.

Thanks to https://rehmat-sayany.medium.com And https://manuel-heidrich.dev

--

--

No responses yet