安装依赖
shell
npm i @nestjs/swagger nest-knife4j -S
在main.ts
中修改配置如下:
ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { knife4jSetup } from 'nest-knife4j';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 配置接口的全局前缀
app.setGlobalPrefix('/api');
// 配置swagger的api文档
// addBearerAuth 用于添加token认证
const config = new DocumentBuilder()
.setTitle('接口文档')
.setDescription('The API description')
.setVersion('1.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('doc', app, document, {
// 增加接口文档的额外配置
swaggerOptions: {
// 添加持久化的token,防止刷新token失效
persistAuthorization: true,
},
});
knife4jSetup(app, [
{
name: '2.X版本',
url: `/doc-json`,
swaggerVersion: '2.0',
location: `/doc-json`,
},
]);
await app.listen(3000);
}
bootstrap();
访问 http://localhost:3000/doc.html
就可以看到 swagger 的 api 文档了,如下图所示
装饰器介绍
- @ApiTags(tags: string[]): 用于给控制器或方法添加标签,用于组织和分类 API 文档
- @ApiOperation(options: OperationOptions): 用于给方法添加操作信息,包括方法的摘要、描述、响应等
- @ApiParam(options: ApiParamOptions): 用于给方法定义请求参数的描述信息
- @ApiQuery(options: ApiQueryOptions): 用于给方法定义查询参数的描述信息
- @ApiBody(options: ApiBodyOptions): 用于给方法定义请求体的内容
- @ApiProperty(options: ApiPropertyOptions): 用于给模型定义各属性的描述信息
- @ApiResponse(options: ApiResponseOptions): 用于给方法添加响应的描述信息
- @ApiHeader(options: ApiHeaderOptions): 用于定义请求头的描述信息
配置示例
ts
// src/modules/user/user.controller.ts
import { Controller, Get } from "@nestjs/common";
import { ApiTags, ApiOperation, ApiResponse } from "@nestjs/swagger";
@Controller("users")
@ApiTags("用户相关接口")
export class UsersController {
constructor(private readonly userService: UserService) {}
@Post("addUser")
@ApiOperation({
summary: "获取用户列表",
// description: '获取所有的用户列表',
})
@ApiResponse({
status: 200,
description: "成功返回200",
schema: {
type: "array",
example: [
{
id: 1,
name: "张三",
age: 18,
gender: 1,
},
],
},
})
addUser(@Body() userData: AddUserDto): UserItem[] {
return this.userService.addUser(userData);
}
}
// src/modules/user/dto/addUser.dto.ts
import { IsNotEmpty, IsString, IsNumber, IsIn } from "class-validator";
import { ApiProperty } from "@nestjs/swagger";
export class AddUserDto {
@IsNotEmpty({ message: "id should not be empty" })
@IsNumber({ allowNaN: false }, { message: "id must be a number" })
@ApiProperty({ example: 1, description: "用户唯一 id" })
id: number;
@IsNotEmpty()
@IsString()
@ApiProperty({ example: "张三", description: "用户名" })
name: string;
@IsNotEmpty()
@IsNumber()
@ApiProperty({ example: 18, description: "用户年龄" })
age: number;
@IsNotEmpty()
@IsIn([1, 2])
@ApiProperty({ example: 1, description: "用户性别: 1 -> 男、2 -> 女" })
gender: 1 | 2;
}
在 entity 实体上使用
ts
todo.entity.ts;
@Entity("todo")
export class TodoEntity {
@Column()
@ApiProperty({ description: "todo" })
value: string;
@ApiProperty({ description: "todo" })
@Column({ default: false })
status: boolean;
}
在 controller 中使用
ts
// todo.controller.ts
@Get()
@ApiOperation({ summary: '获取Todo详情' })
@ApiResponse({ type: [TodoEntity] })
async list(): Promise<TodoEntity[]/> {
return this.todoService.list();
}
@Get(':id')
@ApiOperation({ summary: '获取Todo详情' })
@ApiResponse({ type: TodoEntity })
async info(@IdParam() id: number): Promise<TodoEntity/> {
return this.todoService.detail(id);
}
在 dto 中使用
ts
// todo.dto.ts;
export class Todo {
@ApiProperty({ description: "todo" })
value: string;
@ApiProperty({ description: "todo" })
status: boolean;
}
// src/logical/user/user.dto.ts
import { IsNotEmpty, IsNumber, IsString } from "class-validator";
import { ApiProperty } from "@nestjs/swagger";
export class RegisterInfoDTO {
@ApiProperty()
@IsNotEmpty({ message: "用户名不能为空" })
readonly accountName: string;
@ApiProperty()
@IsNotEmpty({ message: "真实姓名不能为空" })
@IsString({ message: "真实姓名必须是 String 类型" })
readonly realName: string;
@ApiProperty()
@IsNotEmpty({ message: "密码不能为空" })
readonly password: string;
@ApiProperty()
@IsNotEmpty({ message: "重复密码不能为空" })
readonly repassword: string;
@ApiProperty()
@IsNotEmpty({ message: "手机号不能为空" })
@IsNumber()
readonly mobile: number;
@ApiProperty()
readonly role?: string | number;
}
swagger 自定义返回的数据
通常情况下,都会对返回数据进行一层包装,如
json
{
"data": [
{
"name": "string"
}
],
"code": 200,
"message": "success"
}
、其中 data 数据就是原始数据。要实现这种数据结构字段,首先定义一个自定义类用于包装,如
ts
export class ResOp<T = any/> {
@ApiProperty({ type: 'object' })
data?: T;
@ApiProperty({ type: 'number', default: 200 })
code: number;
@ApiProperty({ type: 'string', default: 'success' })
message: string;
constructor(code: number, data: T, message = 'success') {
this.code = code;
this.data = data;
this.message = message;
}
}
接着在定义一个拦截器,将 data 数据用 ResOp 包装,拦截器代码如下
ts
// transform.interceptor.ts
export class TransformInterceptor implements NestInterceptor {
constructor(private readonly reflector: Reflector) {}
intercept(
context: ExecutionContext,
next: CallHandler<any/>,
): Observable<any/> {
return next.handle().pipe(
map((data) => {
const response = context.switchToHttp().getResponse<FastifyReply/>();
response.header('Content-Type', 'application/json; charset=utf-8');
return new ResOp(HttpStatus.OK, data ?? null);
}),
);
}
}
此时返回的数据都会转换为 { "data": { }, "code": 200, "message": "success" } 的形式,这部分不为就本文重点,就不赘述了。 回到 Swagger 文档中,只需要 @ApiResponse({ type: TodoEntity }) 改写成 @ApiResponse({ type: ResOp }),就可以实现下图需求。
添加分组
ts
import { ApiTags } from '@nestjs/swagger';
@Controller('guard')
@ApiTags('守卫接口') // 分组
@UseGuards(RoleGuard)
添加单个接口描述信息
ts
import { ApiOperation } from '@nestjs/swagger';
@Get()
@Role('admin') // 自定义名称 数据
@ApiOperation({ summary: 'get接口', description: '角色' })
findAll(@MyParam() param: string) {
console.log('打印***param', param);
return this.guardService.findAll();
}
Param 参数描述
ts
@Get(':id')
@ApiParam({
name: 'id',
description: '获取单个数据',
required: true,
})
findOne(@Param('id') id: string) {
return this.guardService.findOne(+id);
}
query 参数描述
ts
@Get()
@Role('admin') // 自定义名称 数据
@ApiQuery({
name: 'page',
description: '分页信息',
required: true,
type: String,
})
findAll(@MyParam() param: string) {
console.log('打印***param', param);
return this.guardService.findAll();
}
post 参数装饰
结合 dto 进行使用
create-guard.dto.ts
ts
import { ApiProperty } from '@nestjs/swagger';
export class CreateGuardDto {
@ApiProperty({ example: 'jack', type: String })
name: string;
@ApiProperty()
age: number;
}
=====================post中不用写===================
@Post()
create(@Body() createGuardDto: CreateGuardDto) {
return this.guardService.create(createGuardDto);
}
添加 token 信息
main.ts
ts
const options = new DocumentBuilder()
.addBearerAuth() // 此处添加
.setTitle("接口文档")
.setDescription("描述信息")
.setVersion("1.0.0")
.build();
guard.controller.ts
ts
@Controller("guard")
@ApiTags("守卫接口") // 分组
@ApiBearerAuth()
@UseGuards(RoleGuard)
export class GuardController {}
常用的 Swagger 装饰器
装饰器 | 描述 | 使用场景 |
---|---|---|
@ApiTags | 为控制器或方法添加标签,用于组织 Swagger UI 文档。 | 标明控制器或方法所属的领域,使文档更易于组织。 |
@ApiOperation | 为控制器方法添加操作描述,包括摘要和详细描述。 | 提供关于 API 操作的清晰说明,方便开发者理解 API 的作用。 |
@ApiParam | 描述路径参数、请求参数或响应参数,包括名称、类型、描述等。 | 提供详细的参数信息,方便开发者正确使用和理解 API。 |
@ApiBody | 指定请求体的 DTO 类型,用于描述请求体的结构。 | 明确请求体的结构,帮助开发者正确发送请求。 |
@ApiResponse | 描述 API 的响应,包括状态码、描述等。 | 提供关于 API 响应的详细说明,方便开发者处理各种响应情况。 |
@ApiBearerAuth | 指定请求需要携带 Bearer Token,用于身份验证。 | 在需要身份验证的接口中使用,指定需要提供 Token 信息。 |
@ApiProperty | 为 DTO 类型的属性添加元数据,如描述、默认值等。 | 提供详细的属性信息,使开发者了解 DTO 对象的结构和约束。 |
@ApiQuery | 描述查询参数,包括名称、类型、描述等。 | 用于标识查询参数,使开发者清晰了解 API 的可用查询选项。 |
@ApiHeader | 描述请求头信息,包括名称、类型、描述等。 | 提供请求头的详细信息,使开发者正确设置请求头。 |
@ApiExcludeEndpoint | 标记一个控制器方法不在 Swagger UI 中显示。 | 在一些特殊情况下,可以使用该装饰器排除不需要在文档中展示的接口。 |
增强版 Swagger 文档
由于默认的 swagger 文档看起来会比较混乱,因为我们重点推荐加强版的 swagger。如下
安装
pnpm install swagger-ui-express @nestjs/swagger nest-knife4j --save
在 main.ts 中配置如下:
ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
import { knife4jSetup } from "nest-knife4j";
import { ResponseInterceptor } from "./interceptors/response.interceptor";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 配置swagger的api文档
const config = new DocumentBuilder()
.setTitle("接口文档")
.setDescription("The API description")
.setVersion("1.0")
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup("doc", app, document);
knife4jSetup(app, [
{
name: "2.X版本",
// 请注意:这里的url和location的配置需要参考SwaggerModule.setup('doc', app, document);
// 也就说上面的配置访问路径是/doc,那么下面的两个配置就是/doc-json
url: `/doc-json`,
swaggerVersion: "2.0",
location: `/doc-json`,
},
]);
// 添加全局的响应拦截器
app.useGlobalInterceptors(new ResponseInterceptor());
await app.listen(3000);
}
bootstrap();
访问:
在项目运行起来之后,访问 http://localhost:3000/doc.html
即可