Skip to main content

Nestjs Swagger

Cài đặt thư viện

npm install @nestjs/swagger

Cấu hình Swagger Plugin

info

Plugin Swagger sẽ tự động:

  • Tự động đọc object của request như body, param, query, header và response ở các file mà ta định nghĩa DTO.
  • Thiết lập thuộc tính required tùy thuộc vào dấu "?" ở property (ví dụ: name?: string sẽ đặt required: false)
  • Thiết lập type hoặc enum tùy thuộc vào kiểu ta định nghĩa (hỗ trợ kiểu mảng)
  • Thiết lập giá trị default dựa trên giá trị mặc định được gán
  • Thiết lập một số quy tắc xác thực dựa trên các trình trang trí class-validator
  • Để thiết lập Swagger Plugin, vào file nest-cli.json và thêm plugin như sau:
nest-cli.json
{
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"plugins": ["@nestjs/swagger"]
}
}
  • Ta có thể thêm các cấu hình cho plugin:
"plugins": [
{
"name": "@nestjs/swagger",
"options": {
"classValidatorShim": false,
"introspectComments": true
}
}
]
Tùy chọnGiá trị mặc địnhMô tả
dtoFileNameSuffix['.dto.ts', '.entity.ts']Đuôi file mà plugin sẽ đọc DTO
  • Dưới đây là so sánh khi ta không dùng và dùng Swagger Plugin:
create-user.dto.ts
export class CreateUserDto {
@ApiProperty()
email: string;

@ApiProperty()
password: string;

@ApiProperty({ enum: RoleEnum, default: [], isArray: true })
roles: RoleEnum[] = [];

@ApiProperty({ required: false, default: true })
isEnabled?: boolean = true;
}
create-user.dto.ts
export class CreateUserDto {
email: string;
password: string;
roles: RoleEnum[] = [];
isEnabled?: boolean = true;
}
note
  • Hãy luôn chỉ định kiểu trả về cho hàm controller để NestJS Swagger Plugin có thể tự động tạo ra docs định nghĩa các thuộc tính cho response trả về.

Cấu hình Swagger

  • Tạo file swagger.config.ts:
swagger.config.ts
import { DocumentBuilder, SwaggerCustomOptions } from "@nestjs/swagger";

export const swaggerConfig = new DocumentBuilder()
.setTitle("Task Management APIs")
.setDescription("The Task Management API description")
.setVersion("1.0")
.addBearerAuth({
type: "http",
scheme: "Bearer",
description: "Enter your access token here",
})
.build();

export const swaggerOptions: SwaggerCustomOptions = {
customSiteTitle: "Task Management API",
customCss: ".swagger-ui section.models { display: none}",
customfavIcon: "/public/static/app-logo.svg",
};
note
  • Xem cách cấu hình để serve file tĩnh favIcon tại đây
  • Cuối cùng, ở file main.ts:
main.ts
import { Logger } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { NestFactory } from "@nestjs/core";
import { SwaggerModule } from "@nestjs/swagger";
import { WinstonModule } from "nest-winston";

import { AppModule } from "./app.module";
import { winstonLogger } from "src/common/logger/logger.config";
import {
swaggerConfig,
swaggerOptions,
} from "src/common/swagger/swagger.config";
import { IEnvironmentVariables } from "src/common/types/env.type";

async function bootstrap() {
const app = await NestFactory.create(AppModule, {
logger: WinstonModule.createLogger({ instance: winstonLogger }),
});
const configService = app.get(ConfigService<IEnvironmentVariables>);
const nestServerPort = configService.get<number>("NEST_SERVER_PORT")!;

app.setGlobalPrefix("api/v1");
app.enableCors({ origin: "*" });

const document = SwaggerModule.createDocument(app, swaggerConfig);
SwaggerModule.setup("/", app, document, swaggerOptions);

await app.listen(nestServerPort);
Logger.log(
`Server is running on: http://localhost:${nestServerPort}`,
bootstrap.name
);
}
bootstrap();

Swagger Decorator

@ApiTags()

  • Được sử dụng để gắn thẻ (tag) các endpoints của API nhằm mục đích tổ chức và phân loại chúng trong tài liệu Swagger.
  • Cú pháp:
import { ApiTags } from '@nestjs/swagger';

// Phạm vi sử dụng: đầu "controller"
@ApiTags(name: string)

@ApiResponse()

  • Được sử dụng để mô tả các phản hồi (response) của một API endpoint. Nó giúp tạo tài liệu API rõ ràng và chi tiết hơn bằng cách xác định những loại phản hồi khác nhau mà một endpoint có thể trả về, bao gồm mã trạng thái HTTP và thông tin về kiểu dữ liệu của phản hồi.
  • Cú pháp:
import { ApiResponse } from '@nestjs/swagger';

// Phạm vi sử dụng: đầu "controller" hoặc đầu mỗi "method"
@ApiResponse(options: ApiResponseOptions)
ApiResponseOptionsKiểu dữ liệuMô tả
statusnumber| 'default' | '1XX' | '2XX' | '3XX' | '4XX' | '5XX'Mã phản hồi
typeObject, ClassKiểu dữ liệu của phản hồi, được định nghĩa bằng class
descriptionstringMô tả
  • Ví dụ:
@ApiResponse({ status: 200, description: 'Thành công.', type: SomeDtoClass })
@ApiResponse({ status: 404, description: 'Không tìm thấy dữ liệu.' })
@ApiResponse({ status: '4XX', description: 'Error', type: ExceptionResponse })
  • Ngoài việc sử dụng @ApiResponse(), NestJS Swagger cung cấp cho ta các decorator tương ứng với các mã phản hồi cụ thể dưới đây. Những decorator này đều nhận các options như bên trên:
@ApiOkResponse()
@ApiCreatedResponse()
@ApiAcceptedResponse()
@ApiNoContentResponse()
@ApiMovedPermanentlyResponse()
@ApiFoundResponse()
@ApiBadRequestResponse()
@ApiUnauthorizedResponse()
@ApiNotFoundResponse()
@ApiForbiddenResponse()
@ApiMethodNotAllowedResponse()
@ApiNotAcceptableResponse()
@ApiRequestTimeoutResponse()
@ApiConflictResponse()
@ApiPreconditionFailedResponse()
@ApiTooManyRequestsResponse()
@ApiGoneResponse()
@ApiPayloadTooLargeResponse()
@ApiUnsupportedMediaTypeResponse()
@ApiUnprocessableEntityResponse()
@ApiInternalServerErrorResponse()
@ApiNotImplementedResponse()
@ApiBadGatewayResponse()
@ApiServiceUnavailableResponse()
@ApiGatewayTimeoutResponse()
@ApiDefaultResponse()

@ApiBody()

  • Được sử dụng để mô tả dữ liệu thân (body) của một yêu cầu HTTP đối với các endpoint sử dụng phương thức POST, PUT, hoặc PATCH. Nó giúp tài liệu hóa phần dữ liệu đầu vào của yêu cầu HTTP, bao gồm kiểu dữ liệu và mô tả chi tiết về các trường trong body request.
  • Cú pháp:
import { ApiBody } from '@nestjs/swagger';

// Phạm vi sử dụng: đầu mỗi "method"
@ApiBody(options: ApiBodyOptions)
ApiBodyOptionsKiểu dữ liệuMô tả
typeDTO ClassLớp DTO hoặc kiểu dữ liệu cụ thể của body
descriptionstringMô tả chi tiết nội dung body request
requiredbooleanXác định body có bắt buộc hay không
import { ApiBody } from '@nestjs/swagger';

class CreateUserDto {
name: string;
email: string;
password: string;
}

@ApiBody({
description: 'Dữ liệu tạo mới người dùng',
type: CreateUserDto,
})
@Post('create-user')
createUser(@Body() createUserDto: CreateUserDto) {
return this.userService.createUser(createUserDto);
}

@ApiParam()

  • Được sử dụng để mô tả và tài liệu hóa các tham số (parameters) đường dẫn (path parameters) cho các endpoint trong API. Khi một endpoint có các tham số được định nghĩa trong URL (ví dụ: /users/:id), @ApiParam() giúp hiển thị rõ ràng cách sử dụng tham số đó trong tài liệu Swagger.
  • Cú pháp:
import { ApiParam } from '@nestjs/swagger';

// Phạm vi áp dụng: ở đầu mỗi "method"
@ApiParam(option: ApiParamOptions)
ApiParamOptionsKiểu dữ liệuMô tả
namestringTên param
typestringKiểu dữ liệu param ("string", "number", "boolean",...)
descriptionstringMô tả cho param
requiredbooleanXác định param có bắt buộc hay không
allowEmptyValuebooleanXác định có cho phép param nhận giá trị rỗng hay không
enumenumĐịnh nghĩa enum cho giá trị của param
exampleanyMô tả giá trị ví dụ cụ thể cho param
  • Ví dụ:
@ApiParam({
name: 'userId',
description: 'ID của người dùng',
type: 'string'
})
@ApiParam({
name: 'postId',
description: 'ID của bài viết của người dùng',
type: 'number'
})
@Get(':userId/posts/:postId')
getUserPost(@Param('userId') userId: string, @Param('postId') postId: number) {
return `Thông tin bài viết có ID: ${postId} của người dùng có ID: ${userId}`;
}
  • Ví dụ về sử dụng enum:
import { ApiParam } from "@nestjs/swagger";
import { Controller, Get, Param } from "@nestjs/common";

enum UserRole {
ADMIN = "admin",
USER = "user",
GUEST = "guest",
}

@Controller("users")
export class UserController {
@Get(":role")
@ApiParam({
name: "role",
description: "Vai trò của người dùng (admin, user, guest)",
enum: UserRole, // Chỉ định enum
})
getUserByRole(@Param("role") role: UserRole) {
return `Thông tin người dùng có vai trò: ${role}`;
}
}

@ApiQuery()

  • Được sử dụng để mô tả các tham số truy vấn (query parameters) của một API endpoint. Tham số truy vấn là các thông số được gửi kèm theo URL dưới dạng cặp key-value sau dấu ? (ví dụ: /users?age=25). @ApiQuery() giúp tạo tài liệu rõ ràng cho các query parameters này trong Swagger.
  • Cú pháp:
import { ApiQuery } from '@nestjs/swagger';

// Phạm vi áp dụng: ở đầu mỗi "method"
@ApiQuery(option: ApiQueryOptions)
ApiQueryOptionsKiểu dữ liệuMô tả
namestringTên key của query
typestringKiểu dữ liệu của key query ("string", "number", "boolean",...)
descriptionstringMô tả cho key query
allowEmptyValuebooleanXác định có cho phép key query nhận giá trị rỗng hay không
requiredbooleanXác định key query có bắt buộc hay không
enumenumĐịnh nghĩa enum cho giá trị của key query
exampleanyMô tả giá trị ví dụ cụ thể cho key query
isArraybooleanXác định key query có nhận giá trị là mảng hay không
  • Ví dụ:
@Controller("products")
export class ProductController {
@Get()
@ApiQuery({
name: "category",
description: "Danh mục sản phẩm",
required: true,
enum: ["electronics", "clothing", "books"], // Các giá trị hợp lệ
})
getProductsByCategory(@Query("category") category: string) {
return `Danh sách sản phẩm trong danh mục: ${category}`;
}
}
  • Ví dụ với isArray: true:
@Controller("items")
export class ItemController {
@Get()
@ApiQuery({
name: "ids",
description: "Danh sách ID của các item",
required: false,
isArray: true, // Tham số là một mảng
type: "number",
})
getItemsByIds(@Query("ids") ids: number[]) {
return `Danh sách các item có ID: ${ids.join(", ")}`;
}
}

@ApiHeader() / @ApiHeaders()

  • Được sử dụng để mô tả các header parameters (tham số trong phần header) mà một API endpoint yêu cầu. Những tham số này có thể bao gồm các giá trị như token, API key, hoặc các thông tin khác được gửi trong phần header của yêu cầu HTTP.
  • Cú pháp:
import { ApiHeader, ApiHeaders } from '@nestjs/swagger';

// Phạm vi áp dụng: ở đầu "controller" hoặc đầu mỗi "method"
@ApiHeader(option: ApiHeaderOptions)
@ApiHeader(option: ApiHeaderOptions[])
ApiHeaderOptionsKiểu dữ liệuMô tả
namestringTên key header
descriptionstringMô tả cho key header
requiredbooleanXác định key header có bắt buộc hay không
allowEmptyValuebooleanXác định có cho phép key header nhận giá trị rỗng hay không
enumenumĐịnh nghĩa enum cho giá trị của key header
exampleanyMô tả giá trị ví dụ cụ thể cho key header
  • Ví dụ về @ApiHeader():
@Controller("orders")
export class OrderController {
@Get()
@ApiHeader({
name: "Authorization",
description: "Token xác thực người dùng",
required: true,
})
@ApiHeader({
name: "Client-Id",
description: "ID của client thực hiện yêu cầu",
required: true,
})
getOrders(
@Headers("Authorization") authToken: string,
@Headers("Client-Id") clientId: string
) {
return `Danh sách đơn hàng cho client ${clientId} với token: ${authToken}`;
}
}
  • Ví dụ về @ApiHeaders():
@Controller("orders")
export class OrderController {
@Get()
@ApiHeaders([
{
name: "Authorization",
description: "Token xác thực người dùng",
required: true,
},
{
name: "Client-Id",
description: "ID của client thực hiện yêu cầu",
required: true,
},
])
getOrders(
@Headers("Authorization") authToken: string,
@Headers("Client-Id") clientId: string
) {
return `Danh sách đơn hàng cho client ${clientId} với token: ${authToken}`;
}
}

@ApiBearerAuth()

  • Được sử dụng để định nghĩa và mô tả phương thức xác thực Bearer Authentication cho một hoặc nhiều API endpoints. Bearer Authentication là một phương thức xác thực mà trong đó, một token (thường là JWT - JSON Web Token) được gửi trong header của các yêu cầu HTTP, cụ thể trong header Authorization dưới dạng:
Authorization: Bearer <token>
  • Cú pháp:
import { ApiBearerAuth } from '@nestjs/swagger';

// Phạm vi sử dụng: ở đầu "controller" hoặc đầu mỗi "method"
@ApiBearerAuth()
  • Ví dụ:
@ApiBearerAuth()
@Controller("cats")
export class CatsController {}
caution
  • Nhớ thêm .addBearerAuth() khi cấu hình Swagger:
export const swaggerConfig = new DocumentBuilder()
.setTitle("Task Management APIs")
.setDescription("The Task Management API description")
.setVersion("1.0")
.addBearerAuth({
type: "http",
scheme: "Bearer",
description: "Enter your access token here",
}) // Add this
.build();

@ApiProperty()

  • Được sử dụng để định nghĩa các thuộc tính của DTO.
  • Cú pháp:
import { ApiProperty } from '@nestjs/swagger';

// Phạm vi sử dụng: ở đầu của thuộc tính trong class DTO
@ApiProperty(options?: ApiPropertyOptions)
ApiPropertyOptionsKiểu dữ liệuMô tả
namestringTên thuộc tính
descriptionstringMô tả về thuộc tính
requiredbooleanXác định thuộc tính có bắt buộc hay không
nullablebooleanXác định thuộc tính có nhận giá trị null hay không
isArraybooleanXác định thuộc tính có là mảng hay không
uniqueItemsbooleanXác định thuộc tính có là mảng chứa các phần tử riêng biệt hay không
typestringKiểu dữ liệu của thuộc tính ("string", "number", "boolean",...)
enumenumĐịnh nghĩa giá trị enum cho thuộc tính
exampleanyĐịnh nghĩa ví dụ giá trị cho thuộc tính
formatstringChỉ định định dạng của giá trị thuộc tính. Các giá trị có thể nhận của format: "date", "date-time", "binary", "email", "uuid", "hostname", "ipv4", "ipv6"
minimumnumberGiá trị tối thiểu (nếu có type: "number")
maximumnumberGiá trị tối đa (nếu có type: "number")
minItemsnumberSố lượng phần tử tối thiểu trong mảng (áp dụng nếu isArray: true)
maxItemsnumberSố lượng phần tử tối đa trong mảng (áp dụng nếu isArray: true)
minLengthnumberĐộ dài tối thiểu cho chuỗi (áp dụng nếu type: "string")
maxLengthnumberĐộ dài tối đa cho chuỗi (áp dụng nếu type: "string")

@ApiConsumes()

  • Được sử dụng để chỉ định các loại MIME (Content-Type) mà một endpoint API có thể xử lý. Nó thường được sử dụng trong bối cảnh của các phương thức HTTP POST hoặc PUT để chỉ rõ rằng server mong đợi nhận dữ liệu theo định dạng cụ thể.
  • Cú pháp:
import { ApiConsumes } from '@nestjs/swagger';

// Phạm vi sử dụng: ở đầu "controller" hoặc đầu mỗi "method"
@ApiConsumes('application/json')
@ApiConsumes('multipart/form-data') // Áp dụng cho form data được gửi lên từ client
  • Dưới đây là ví dụ về cách sử dụng @ApiConsumes('multipart/form-data')@ApiProperty({ format: 'binary' }) trong NestJS với Swagger hỗ trợ việc upload file:
import {
Controller,
Post,
UseInterceptors,
UploadedFile,
Body,
} from "@nestjs/common";
import { ApiConsumes, ApiResponse, ApiProperty } from "@nestjs/swagger";
import { FileInterceptor } from "@nestjs/platform-express";

class UploadFileDto {
// Định nghĩa một thuộc tính nhận 1 File
@ApiProperty({
type: "string",
format: "binary", // chỉ định định dạng nhị phân
description: "The file to upload",
})
file: Express.Multer.File;

// Định nghĩa một thuộc tính nhận nhiều File
@ApiProperty({
type: "string",
format: "binary", // chỉ định định dạng nhị phân
isArray: true,
description: "The files to upload",
})
files: Express.Multer.File;
}

@Controller("upload")
export class UploadController {
@Post()
@ApiConsumes("multipart/form-data") // chỉ định loại MIME
uploadFile(
@Body() uploadFileDto: UploadFileDto // thông tin bổ sung
) {
// Logic để xử lý tệp và thông tin
}
}

@ApiExtraModels()

  • Trong NestJS + Swagger, decorator @ApiExtraModels() được sử dụng để đăng ký các mô hình (model) phụ bổ sung mà Swagger có thể không tự động phát hiện ra trong các API endpoint. Điều này thường cần thiết khi ta sử dụng các wrapper class hoặc generic types như ApiResponse, Pagination<T>, ResultDto<T>...
  • 📌 Mục đích chính: Swagger (cụ thể là thư viện @nestjs/swagger) chỉ quét và tạo tài liệu cho các model được tham chiếu trực tiếp trong các decorator như @ApiResponse(), @ApiBody(), v.v. Nếu ta dùng các kiểu generic hoặc lớp không được tham chiếu trực tiếp, ta phải dùng @ApiExtraModels() để đảm bảo Swagger biết đến và sinh schema cho chúng.
  • 📘 Cú pháp:
import { ApiExtraModels } from "@nestjs/swagger";

@ApiExtraModels(ModelA, ModelB, PaginationDto)
@Controller("example")
export class ExampleController {
// ...
}
  • 📍 Ví dụ cụ thể:
  • Giả sử ta có một class kết quả chung như sau:
export class ResultDto<T> {
success: boolean;
data: T;
}
  • Và ta có một UserDto:
export class UserDto {
id: number;
name: string;
}
  • Khi ta muốn trả về ResultDto<UserDto>, Swagger không tự biết phải sinh schema của UserDto nằm trong ResultDto, nên ta cần:
import { ApiExtraModels, ApiOkResponse, getSchemaPath } from "@nestjs/swagger";

@ApiExtraModels(ResultDto, UserDto)
@Controller("users")
export class UsersController {
@Get()
@ApiOkResponse({
description: "Get user response",
schema: {
allOf: [
{ $ref: getSchemaPath(ResultDto) },
{
properties: {
data: { $ref: getSchemaPath(UserDto) },
},
},
],
},
})
getUser() {
return {
success: true,
data: { id: 1, name: "John" },
};
}
}
tip

✅ Khi nào nên dùng @ApiExtraModels?

  • Khi dùng các lớp wrapper chung như: Pagination<T>, ResultDto<T>, ResponseWrapper<T>
  • Khi Swagger không tự tạo được schema cho class ta dùng trong response hoặc body.
  • Khi dùng oneOf, allOf, anyOf trong schema và có các model con bên trong.
tip
  • Như đã nói ở trên, do ta vừa cấu hình NestJS Swagger Plugin nên nó sẽ tự động đọc object của request như body, param, query, header và response ở các request object decorator do NestJS cung cấp, ở các file .dto.ts mà ta định nghĩa các DTO. Vì vậy, ta không cần sử dụng những decorator như @ApiBody(), @ApiQuery(), @ApiHeader(), @ApiProperty(). Nhưng nếu ta cần cấu hình thêm như description, enum,... ta vẫn có thể sử dụng các decorator đó để định nghĩa thêm.