NestJS API文档
学习目标
- 掌握NestJS Swagger模块的使用方法
- 理解OpenAPI规范的基本概念
- 学习如何使用API装饰器注释API端点
- 了解如何配置和自定义Swagger文档
- 掌握API版本控制的实现方法
核心知识点
1. Swagger简介
Swagger(现在称为OpenAPI)是一种用于描述和文档化RESTful API的规范。它允许我们:
- 自动生成API文档
- 提供交互式API测试界面
- 定义API的请求和响应格式
- 描述API的认证方式
- 支持API版本控制
在NestJS中,Swagger集成通过@nestjs/swagger包提供。
2. 安装和配置
首先,我们需要安装Swagger模块:
npm install --save @nestjs/swagger swagger-ui-express然后,在应用的主文件中配置Swagger:
// src/main.ts
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 config = new DocumentBuilder()
.setTitle('NestJS API')
.setDescription('The NestJS API description')
.setVersion('1.0')
.addTag('nestjs')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(3000);
}
bootstrap();3. 基本使用
3.1 为控制器添加装饰器
// src/app.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
import { AppService } from './app.service';
import { CreateCatDto } from './dto/create-cat.dto';
import { Cat } from './interfaces/cat.interface';
@ApiTags('cats')
@Controller('cats')
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
@ApiOperation({ summary: '获取所有猫' })
@ApiResponse({ status: 200, description: '成功获取所有猫' })
getCats(): Cat[] {
return this.appService.getCats();
}
@Get(':id')
@ApiOperation({ summary: '根据ID获取猫' })
@ApiParam({ name: 'id', description: '猫的ID' })
@ApiResponse({ status: 200, description: '成功获取猫' })
@ApiResponse({ status: 404, description: '猫不存在' })
getCat(@Param('id') id: string): Cat {
return this.appService.getCat(id);
}
@Post()
@ApiOperation({ summary: '创建猫' })
@ApiBody({ type: CreateCatDto })
@ApiResponse({ status: 201, description: '成功创建猫' })
@ApiResponse({ status: 400, description: '请求数据无效' })
createCat(@Body() createCatDto: CreateCatDto): Cat {
return this.appService.createCat(createCatDto);
}
}3.2 为数据传输对象(DTO)添加装饰器
// src/dto/create-cat.dto.ts
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty({ description: '猫的名字', example: 'Tom' })
name: string;
@ApiProperty({ description: '猫的年龄', example: 3 })
age: number;
@ApiProperty({ description: '猫的品种', example: 'Persian' })
breed: string;
}3.3 为接口添加装饰器
// src/interfaces/cat.interface.ts
import { ApiProperty } from '@nestjs/swagger';
export class Cat {
@ApiProperty({ description: '猫的ID', example: '1' })
id: string;
@ApiProperty({ description: '猫的名字', example: 'Tom' })
name: string;
@ApiProperty({ description: '猫的年龄', example: 3 })
age: number;
@ApiProperty({ description: '猫的品种', example: 'Persian' })
breed: string;
}4. Swagger配置选项
NestJS的Swagger模块提供了丰富的配置选项,我们可以通过DocumentBuilder类来配置:
setTitle():设置文档标题setDescription():设置文档描述setVersion():设置API版本addTag():添加API标签addBearerAuth():添加Bearer认证addApiKey():添加API密钥认证addBasicAuth():添加基本认证addOAuth2():添加OAuth2认证setTermsOfService():设置服务条款链接setContact():设置联系信息setLicense():设置许可证信息
5. API装饰器
NestJS的Swagger模块提供了以下常用的API装饰器:
5.1 控制器装饰器
@ApiTags():为控制器添加标签@ApiBearerAuth():为控制器添加Bearer认证@ApiBasicAuth():为控制器添加基本认证@ApiOAuth2():为控制器添加OAuth2认证
5.2 方法装饰器
@ApiOperation():描述API操作@ApiResponse():描述API响应@ApiParam():描述API路径参数@ApiQuery():描述API查询参数@ApiBody():描述API请求体@ApiHeader():描述API请求头@ApiExcludeEndpoint():排除API端点
5.3 属性装饰器
@ApiProperty():描述模型属性@ApiPropertyOptional():描述可选的模型属性@ApiHideProperty():隐藏模型属性
6. 高级配置
6.1 自定义Swagger UI
我们可以通过传递选项对象来自定义Swagger UI:
// src/main.ts
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 config = new DocumentBuilder()
.setTitle('NestJS API')
.setDescription('The NestJS API description')
.setVersion('1.0')
.addTag('nestjs')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document, {
swaggerOptions: {
filter: true,
showRequestDuration: true,
defaultModelsExpandDepth: -1,
},
});
await app.listen(3000);
}
bootstrap();6.2 多个Swagger文档
我们可以为不同的API版本或模块创建多个Swagger文档:
// src/main.ts
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);
// v1 API文档
const configV1 = new DocumentBuilder()
.setTitle('NestJS API v1')
.setDescription('The NestJS API v1 description')
.setVersion('1.0')
.addTag('v1')
.build();
const documentV1 = SwaggerModule.createDocument(app, configV1, {
include: [AppModule],
});
SwaggerModule.setup('api/v1', app, documentV1);
// v2 API文档
const configV2 = new DocumentBuilder()
.setTitle('NestJS API v2')
.setDescription('The NestJS API v2 description')
.setVersion('2.0')
.addTag('v2')
.build();
const documentV2 = SwaggerModule.createDocument(app, configV2, {
include: [AppModule],
});
SwaggerModule.setup('api/v2', app, documentV2);
await app.listen(3000);
}
bootstrap();7. API版本控制
7.1 路径版本控制
我们可以通过在路由路径中添加版本前缀来实现API版本控制:
// src/app.module.ts
import { Module } from '@nestjs/common';
import { CatsV1Controller } from './cats-v1.controller';
import { CatsV2Controller } from './cats-v2.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsV1Controller, CatsV2Controller],
providers: [CatsService],
})
export class AppModule {}// src/cats-v1.controller.ts
import { Controller, Get } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { CatsService } from './cats.service';
import { CatV1 } from './interfaces/cat-v1.interface';
@ApiTags('cats v1')
@Controller('v1/cats')
export class CatsV1Controller {
constructor(private readonly catsService: CatsService) {}
@Get()
getCats(): CatV1[] {
return this.catsService.getCatsV1();
}
}// src/cats-v2.controller.ts
import { Controller, Get } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { CatsService } from './cats.service';
import { CatV2 } from './interfaces/cat-v2.interface';
@ApiTags('cats v2')
@Controller('v2/cats')
export class CatsV2Controller {
constructor(private readonly catsService: CatsService) {}
@Get()
getCats(): CatV2[] {
return this.catsService.getCatsV2();
}
}7.2 头部版本控制
我们可以通过自定义中间件来实现基于头部的API版本控制:
// src/common/middleware/version.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class VersionMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const version = req.headers['x-api-version'] || '1';
req.url = `/v${version}${req.url}`;
next();
}
}然后在应用模块中使用:
// src/app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { CatsV1Controller } from './cats-v1.controller';
import { CatsV2Controller } from './cats-v2.controller';
import { CatsService } from './cats.service';
import { VersionMiddleware } from './common/middleware/version.middleware';
@Module({
controllers: [CatsV1Controller, CatsV2Controller],
providers: [CatsService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(VersionMiddleware)
.forRoutes('cats');
}
}实用案例分析
案例1:完整的API文档系统
需求分析
我们需要实现一个完整的API文档系统,包括:
- 为所有API端点生成文档
- 支持Bearer认证
- 提供交互式API测试界面
- 实现API版本控制
- 自定义Swagger UI配置
实现方案
- 安装所需依赖:
npm install --save @nestjs/swagger swagger-ui-express @nestjs/jwt passport-jwt- 创建认证模块:
// src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
import { UsersModule } from '../users/users.module';
@Module({
imports: [
UsersModule,
PassportModule,
JwtModule.register({
secret: process.env.JWT_SECRET || 'secretKey',
signOptions: { expiresIn: '60s' },
}),
],
providers: [AuthService, JwtStrategy],
exports: [AuthService],
})
export class AuthModule {}- 创建认证服务:
// src/auth/auth.service.ts
import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService,
) {}
async validateUser(username: string, pass: string): Promise<any> {
const user = await this.usersService.findOneByUsername(username);
if (user && user.password === pass) {
const { password, ...result } = user;
return result;
}
return null;
}
async login(user: any) {
const payload = { username: user.username, sub: user.userId };
return {
access_token: this.jwtService.sign(payload),
};
}
}- 创建JWT策略:
// src/auth/jwt.strategy.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { AuthService } from './auth.service';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.JWT_SECRET || 'secretKey',
});
}
async validate(payload: any) {
const user = await this.authService.validateUser(payload.username, '');
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}- 创建用户模块:
// src/users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
@Module({
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}- 创建用户服务:
// src/users/users.service.ts
import { Injectable } from '@nestjs/common';
import { User } from './interfaces/user.interface';
@Injectable()
export class UsersService {
private readonly users: User[] = [
{ userId: 1, username: 'john', password: 'changeme' },
{ userId: 2, username: 'chris', password: 'secret' },
{ userId: 3, username: 'maria', password: 'guess' },
];
async findOneByUsername(username: string): Promise<User | undefined> {
return this.users.find(user => user.username === username);
}
async findAll(): Promise<User[]> {
return this.users;
}
}- 创建用户控制器:
// src/users/users.controller.ts
import { Controller, Get, Post, Body, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { ApiTags, ApiOperation, ApiResponse, ApiBearerAuth } from '@nestjs/swagger';
import { UsersService } from './users.service';
import { User } from './interfaces/user.interface';
import { LoginDto } from './dto/login.dto';
import { AuthService } from '../auth/auth.service';
@ApiTags('users')
@Controller('users')
export class UsersController {
constructor(
private readonly usersService: UsersService,
private readonly authService: AuthService,
) {}
@Post('login')
@ApiOperation({ summary: '用户登录' })
@ApiResponse({ status: 200, description: '登录成功,返回token' })
@ApiResponse({ status: 401, description: '用户名或密码错误' })
async login(@Body() loginDto: LoginDto) {
const user = await this.usersService.findOneByUsername(loginDto.username);
if (!user || user.password !== loginDto.password) {
throw new UnauthorizedException('用户名或密码错误');
}
return this.authService.login(user);
}
@Get()
@UseGuards(AuthGuard('jwt'))
@ApiOperation({ summary: '获取所有用户' })
@ApiBearerAuth()
@ApiResponse({ status: 200, description: '成功获取所有用户' })
@ApiResponse({ status: 401, description: '未授权' })
async findAll(): Promise<User[]> {
return this.usersService.findAll();
}
}- 创建数据传输对象:
// src/users/dto/login.dto.ts
import { ApiProperty } from '@nestjs/swagger';
export class LoginDto {
@ApiProperty({ description: '用户名', example: 'john' })
username: string;
@ApiProperty({ description: '密码', example: 'changeme' })
password: string;
}- 创建用户接口:
// src/users/interfaces/user.interface.ts
import { ApiProperty } from '@nestjs/swagger';
export class User {
@ApiProperty({ description: '用户ID', example: 1 })
userId: number;
@ApiProperty({ description: '用户名', example: 'john' })
username: string;
@ApiProperty({ description: '密码', example: 'changeme' })
password: string;
}- 配置Swagger:
// src/main.ts
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 config = new DocumentBuilder()
.setTitle('NestJS API')
.setDescription('The NestJS API description')
.setVersion('1.0')
.addTag('users')
.addBearerAuth({
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
name: 'JWT',
description: 'Enter JWT token',
in: 'header',
})
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document, {
swaggerOptions: {
filter: true,
showRequestDuration: true,
persistAuthorization: true,
},
});
await app.listen(3000);
}
bootstrap();- 创建应用模块:
// src/app.module.ts
import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module';
import { AuthModule } from './auth/auth.module';
@Module({
imports: [UsersModule, AuthModule],
})
export class AppModule {}常见问题与解决方案
1. Swagger文档不显示
可能原因:
- Swagger模块未正确配置
- API装饰器使用错误
- 路径冲突
解决方案:
- 检查Swagger模块配置是否正确
- 确保所有API端点都添加了适当的装饰器
- 检查Swagger UI路径是否与其他路由冲突
2. 认证不工作
可能原因:
- 认证装饰器使用错误
- JWT配置错误
- Swagger UI认证设置错误
解决方案:
- 确保在控制器或方法上添加了
@ApiBearerAuth()装饰器 - 检查JWT配置是否正确
- 在Swagger UI中正确设置认证令牌
3. 模型定义不显示
可能原因:
- 模型类未添加
@ApiProperty()装饰器 - 模型类未被Swagger模块扫描到
- 类型引用错误
解决方案:
- 确保所有模型属性都添加了
@ApiProperty()装饰器 - 确保模型类被正确导入和使用
- 检查类型引用是否正确
4. API版本控制冲突
可能原因:
- 路由路径冲突
- 中间件配置错误
- Swagger文档配置错误
解决方案:
- 确保不同版本的API使用不同的路由路径
- 检查中间件配置是否正确
- 为不同版本的API创建单独的Swagger文档
最佳实践
- 一致的命名规范:使用一致的命名规范为API端点、模型和属性命名
- 详细的文档:为所有API端点添加详细的描述和示例
- 认证集成:正确集成认证机制到Swagger文档中
- 版本控制:为API实现合理的版本控制策略
- 错误处理:为所有API端点添加适当的错误响应描述
- 模型验证:使用DTO和验证装饰器确保API数据的有效性
- 自定义UI:根据需要自定义Swagger UI配置
- 测试集成:使用Swagger UI测试API端点
代码优化建议
- 使用配置服务:将Swagger配置放到配置服务中,便于管理
- 创建装饰器工厂:创建自定义装饰器工厂简化重复的装饰器使用
- 使用全局前缀:为API添加全局前缀,便于路由管理
- 实现文档版本控制:为不同版本的API创建不同的Swagger文档
- 添加API标签:使用标签组织API端点,提高文档可读性
总结
NestJS的Swagger模块提供了一种简洁、高效的方式来创建和管理API文档。通过本文的学习,你应该已经掌握了:
- 如何安装和配置Swagger模块
- 如何使用API装饰器注释API端点和模型
- 如何配置和自定义Swagger文档
- 如何实现API版本控制
- 如何集成认证机制到Swagger文档中
API文档是现代API开发的重要组成部分,它可以帮助开发者和使用者更好地理解和使用API。合理使用NestJS的Swagger集成,可以大大提高API的可维护性和可用性。
互动问答
以下哪个是NestJS Swagger模块的正确安装命令?
A.npm install --save @nestjs/swagger
B.npm install --save swagger
C.npm install --save @nestjs/swagger swagger-ui-express
D.npm install --save openapi如何为API端点添加描述?
如何为模型属性添加示例值?
如何在Swagger文档中集成Bearer认证?
如何实现API版本控制?