SwaggerModule tìm tất cả decorator @Body(), @Query(), và @Param() trong route handler để tạo API document. Nó cũng tạo ra các định nghĩa model tương ứng bằng cách tận dụng reflection. Xem đoạn code sau:
@Post()
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
Gợi ý Để thiết lập rõ ràng định nghĩa body, dùng decorator @ApiBody() (import từ package @nestjs/swagger).
Dựa trên CreateCatDto, định nghĩa model sau sẽ được tạo trên Swagger UI:

Như bạn thấy, định nghĩa này trống dù class có khai báo vài property. Để các property của class hiển thị được với SwaggerModule, chúng ta phải annotate chúng bằng decorator @ApiProperty(), hoặc dùng CLI plugin (đọc thêm ở phần Plugin) — plugin này sẽ tự động làm việc đó:
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
@ApiProperty()
age: number;
@ApiProperty()
breed: string;
}
Gợi ý Thay vì phải annotate từng property bằng tay, hãy xem xét dùng Swagger plugin (xem phần Plugin) — nó sẽ tự động làm điều này cho bạn.
Hãy mở browser để kiểm tra model CreateCatDto được tạo ra:

Ngoài ra, decorator @ApiProperty() còn cho phép bạn thiết lập nhiều thuộc tính của Schema Object:
@ApiProperty({
description: 'The age of a cat',
minimum: 1,
default: 1,
})
age: number;
Gợi ý Thay vì viết rõ {{"@ApiProperty({ required: false })"}}, bạn có thể dùng decorator viết tắt @ApiPropertyOptional().
Để thiết lập rõ ràng type của property, dùng key type:
@ApiProperty({
type: Number,
})
age: number;
Array (mảng)
Khi property là một mảng, chúng ta phải chỉ định thủ công type của mảng như sau:
@ApiProperty({ type: [String] })
names: string[];
Gợi ý Hãy xem xét dùng Swagger plugin (xem phần Plugin) — nó sẽ tự động phát hiện mảng.
Bạn có thể đưa type vào làm phần tử đầu tiên của một mảng (như trên), hoặc thiết lập property isArray thành true.
Phụ thuộc vòng (Circular dependencies)
Khi bạn có phụ thuộc vòng giữa các class, dùng một lazy function để cung cấp thông tin type cho SwaggerModule:
@ApiProperty({ type: () => Node })
node: Node;
Gợi ý Hãy xem xét dùng Swagger plugin (xem phần Plugin) — nó sẽ tự động phát hiện phụ thuộc vòng.
Ghi chú người dịch Đây là hệ quả trực tiếp của cách TypeScript compile decorator: metadata type được đọc tại thời điểm class được định nghĩa (module-load time), nên nếu Node tham chiếu ngược lại chính class đang khai báo (hoặc một class khác chưa được định nghĩa xong), giá trị đọc được có thể là undefined. Bọc trong một arrow function (() => Node) trì hoãn việc đọc class đó đến lúc cần dùng thật (lazy evaluation), giống cách bạn dùng forward reference trong nhiều ngôn ngữ khác khi hai type phụ thuộc lẫn nhau.
Generic và interface
Vì TypeScript không lưu metadata về generic hay interface, khi bạn dùng chúng trong DTO, SwaggerModule có thể không tạo được định nghĩa model đúng tại runtime. Ví dụ, đoạn code sau sẽ không được module Swagger kiểm tra (inspect) đúng cách:
createBulk(@Body() usersDto: CreateUserDto[])
Để khắc phục hạn chế này, bạn có thể thiết lập type một cách rõ ràng:
@ApiBody({ type: [CreateUserDto] })
createBulk(@Body() usersDto: CreateUserDto[])
Ghi chú người dịch Đây là giới hạn quen thuộc với ai từng dùng reflection ở Go hay Java: generic ở TypeScript chỉ tồn tại lúc compile-time (type erasure), không còn dấu vết gì ở JS runtime. Decorator dựa vào reflect-metadata, mà thứ này chỉ đọc được type “thô” sau khi compile (ví dụ Array, không phải CreateUserDto[]), nên với generic bạn luôn phải khai báo type tường minh thay vì để Nest tự suy luận.
Enum
Để nhận diện một enum, chúng ta phải thiết lập thủ công property enum trên @ApiProperty với một mảng giá trị.
@ApiProperty({ enum: ['Admin', 'Moderator', 'User']})
role: UserRole;
Hoặc, định nghĩa một enum TypeScript thật như sau:
export enum UserRole {
Admin = 'Admin',
Moderator = 'Moderator',
User = 'User',
}
Sau đó bạn có thể dùng enum này trực tiếp với decorator parameter @Query(), kết hợp với decorator @ApiQuery().
@ApiQuery({ name: 'role', enum: UserRole })
async filterByRole(@Query('role') role: UserRole = UserRole.User) {}

Khi isArray được đặt thành true, enum có thể được chọn kiểu multi-select (chọn nhiều):

Schema cho enum
Theo mặc định, property enum sẽ thêm một định nghĩa thô của Enum vào parameter.
- breed:
type: 'string'
enum:
- Persian
- Tabby
- Siamese
Đặc tả trên hoạt động tốt trong hầu hết trường hợp. Tuy nhiên, nếu bạn dùng một công cụ nhận đặc tả làm input và sinh ra code client-side, bạn có thể gặp vấn đề vì code sinh ra chứa enum bị trùng lặp. Xem đoạn code sau:
// generated client-side code
export class CatDetail {
breed: CatDetailEnum;
}
export class CatInformation {
breed: CatInformationEnum;
}
export enum CatDetailEnum {
Persian = 'Persian',
Tabby = 'Tabby',
Siamese = 'Siamese',
}
export enum CatInformationEnum {
Persian = 'Persian',
Tabby = 'Tabby',
Siamese = 'Siamese',
}
Gợi ý Đoạn code trên được sinh ra bằng một công cụ gọi là NSwag.
Bạn có thể thấy giờ có hai enum giống nhau hoàn toàn.
Để giải quyết vấn đề này, bạn có thể truyền một enumName cùng với property enum trong decorator.
export class CatDetail {
@ApiProperty({ enum: CatBreed, enumName: 'CatBreed' })
breed: CatBreed;
}
Property enumName cho phép @nestjs/swagger biến CatBreed thành schema riêng của nó, từ đó giúp CatBreed enum có thể tái sử dụng. Đặc tả sẽ trông như sau:
CatDetail:
type: 'object'
properties:
...
- breed:
schema:
$ref: '#/components/schemas/CatBreed'
CatBreed:
type: string
enum:
- Persian
- Tabby
- Siamese
Gợi ý Bất kỳ decorator nào nhận enum làm property cũng sẽ nhận enumName.
Ví dụ giá trị cho property
Bạn có thể thiết lập một ví dụ (example) đơn cho property bằng key example, như sau:
@ApiProperty({
example: 'persian',
})
breed: string;
Nếu muốn cung cấp nhiều ví dụ, bạn có thể dùng key examples, truyền vào một object có cấu trúc như sau:
@ApiProperty({
examples: {
Persian: { value: 'persian' },
Tabby: { value: 'tabby' },
Siamese: { value: 'siamese' },
'Scottish Fold': { value: 'scottish_fold' },
},
})
breed: string;
Định nghĩa thô (Raw definitions)
Trong một số trường hợp, chẳng hạn với mảng lồng sâu (deeply nested arrays) hoặc ma trận, bạn có thể cần định nghĩa type một cách thủ công:
@ApiProperty({
type: 'array',
items: {
type: 'array',
items: {
type: 'number',
},
},
})
coords: number[][];
Bạn cũng có thể chỉ định schema object thô, như sau:
@ApiProperty({
type: 'object',
properties: {
name: {
type: 'string',
example: 'Error'
},
status: {
type: 'number',
example: 400
}
},
required: ['name', 'status']
})
rawDefinition: Record<string, any>;
Để định nghĩa thủ công nội dung input/output trong các class controller, dùng property schema:
@ApiBody({
schema: {
type: 'array',
items: {
type: 'array',
items: {
type: 'number',
},
},
},
})
async create(@Body() coords: number[][]) {}
Model bổ sung (Extra models)
Để định nghĩa các model bổ sung không được tham chiếu trực tiếp trong controller của bạn nhưng vẫn cần được module Swagger kiểm tra (inspect), dùng decorator @ApiExtraModels():
@ApiExtraModels(ExtraModel)
export class CreateCatDto {}
Gợi ý Bạn chỉ cần dùng @ApiExtraModels() một lần cho một model class cụ thể.
Ngoài ra, bạn có thể truyền một object tùy chọn với property extraModels được chỉ định cho method SwaggerModule.createDocument(), như sau:
const documentFactory = () =>
SwaggerModule.createDocument(app, options, {
extraModels: [ExtraModel],
});
Để lấy một tham chiếu ($ref) đến model của bạn, dùng function getSchemaPath(ExtraModel):
'application/vnd.api+json': {
schema: { $ref: getSchemaPath(ExtraModel) },
},
oneOf, anyOf, allOf
Để kết hợp các schema, bạn có thể dùng các từ khóa oneOf, anyOf hoặc allOf (đọc thêm).
@ApiProperty({
oneOf: [
{ $ref: getSchemaPath(Cat) },
{ $ref: getSchemaPath(Dog) },
],
})
pet: Cat | Dog;
Nếu bạn muốn định nghĩa một mảng đa hình (polymorphic array — tức mảng mà các phần tử của nó trải trên nhiều schema khác nhau), bạn nên dùng một định nghĩa thô (xem ở trên) để định nghĩa type bằng tay.
type Pet = Cat | Dog;
@ApiProperty({
type: 'array',
items: {
oneOf: [
{ $ref: getSchemaPath(Cat) },
{ $ref: getSchemaPath(Dog) },
],
},
})
pets: Pet[];
Gợi ý Function getSchemaPath() được import từ @nestjs/swagger.
Cả Cat và Dog đều phải được định nghĩa là model bổ sung bằng decorator @ApiExtraModels() (ở cấp class).
Tên và mô tả schema
Như bạn có thể để ý, tên của schema được tạo ra dựa trên tên của model class gốc (ví dụ, model CreateCatDto sẽ tạo ra schema CreateCatDto). Nếu bạn muốn đổi tên schema, bạn có thể dùng decorator @ApiSchema().
Ví dụ:
@ApiSchema({ name: 'CreateCatRequest' })
class CreateCatDto {}
Model trên sẽ được dịch (translate) thành schema CreateCatRequest.
Theo mặc định, không có mô tả nào được thêm vào schema được tạo ra. Bạn có thể thêm một mô tả bằng attribute description:
@ApiSchema({ description: 'Description of the CreateCatDto schema' })
class CreateCatDto {}
Nhờ đó, mô tả sẽ được đưa vào schema, như sau:
schemas:
CreateCatDto:
type: object
description: Description of the CreateCatDto schema