Skip to content

安装依赖

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 即可