前言:为什么写这篇?
最近在转全栈,学习 nest 在做 ai-knowledge 个人知识库AI助手项目时,用 NestJS 搭完接口后,不知道怎么测试输出接口文档,试了 Swagger 后直接解放双手 —— 自动生成文档 + 在线测试,不用再手动写接口说明,Postman 都省了!这篇就把实战中踩过的坑、总结的技巧全分享给大家,新手也能直接抄作业~
目录
1. Swagger 是什么?一句话搞懂
Swagger(也叫 OpenAPI)是 API 文档自动生成工具 + 在线测试平台,配置好后:
- 访问 http://localhost:3000/api 就能看到所有接口的可视化文档
- 直接在页面填参数、点按钮测试,不用 Postman/curl,前后端联调效率翻倍
- 自动同步代码变更,改接口后文档实时更新,再也不用手动维护!
2. 安装与基础配置|3 步搞定
2.1 先装依赖
用 pnpm/npm/yarn 都行,我习惯用 pnpm:
pnpm add @nestjs/swagger
2.2 配置 main.ts(核心步骤)
在项目入口文件加 Swagger 配置,复制粘贴改改标题就行:
// main.ts
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Swagger 核心配置
const swaggerConfig = new DocumentBuilder()
.setTitle('AI Knowledge API') // 文档标题(自定义)
.setDescription('AI 知识库系统接口文档|前端鱼姐实战版') // 描述
.setVersion('1.0') // 接口版本
.addTag('document') // 接口分组标签(后续用)
.addTag('knowledge-base') // 可以加多个分组
.build();
// 创建文档+挂载到 /api 路径
const document = SwaggerModule.createDocument(app, swaggerConfig);
SwaggerModule.setup('api', app, document); // 访问地址:http://localhost:3000/api
await app.listen(3000);
}
2.3 验证是否成功
启动项目 pnpm start:dev,浏览器打开 http://localhost:3000/api,能看到 Swagger UI 页面就搞定啦~
3. 核心装饰器|按场景用就对了
Swagger 靠装饰器生成文档,分 Controller 装饰器(描述接口)和 DTO 装饰器(描述参数),记熟这几个就够日常用!
3.1 Controller 装饰器(接口层面)
给 Controller 类和接口方法加注解,控制文档的分组、说明、参数类型:
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/swagger';
@ApiTags('document') // 分组标签,Swagger 里按这个分类显示
@Controller('document-parse')
export class DocumentController {
// POST 接口:创建文档
@Post()
@ApiOperation({ summary: '创建文档' }) // 接口简短说明
@ApiResponse({ status: 201, description: '创建成功~' }) // 成功响应
@ApiResponse({ status: 400, description: '参数填错啦!' }) // 错误响应
create(@Body() dto: CreateDocumentDto) {
return this.service.create(dto);
}
// GET 接口:通过 ID 查文档
@Get(':id')
@ApiOperation({ summary: '获取单个文档' })
@ApiParam({ name: 'id', description: '文档唯一ID' }) // 路径参数(:id)
findOne(@Param('id') id: string) {
return this.service.findOne(id);
}
// GET 接口:搜索文档
@Get()
@ApiOperation({ summary: '搜索文档' })
@ApiQuery({ name: 'keyword', required: false, description: '搜索关键词' }) // 查询参数(?keyword=xxx)
search(@Query('keyword') keyword: string) {
return this.service.search(keyword);
}
}
3.2 DTO 装饰器(参数层面)
DTO 是接口的参数模板,既要用 class-validator 做数据校验,也要用 Swagger 装饰器做文档说明 ——两个都要写,少一个就踩坑!
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsString, IsOptional, IsNotEmpty } from 'class-validator';
export class CreateDocumentDto {
// 必选字段:文件名
@ApiProperty({
description: '文件名(含后缀)',
example: 'readme.pdf' // Swagger 页面会预填这个示例,超贴心~
})
@IsString() // 校验:必须是字符串
@IsNotEmpty() // 校验:不能为空
fileName: string;
// 可选字段:备注
@ApiPropertyOptional({ description: '文档备注(可选)' }) // 可选字段用这个装饰器
@IsString()
@IsOptional() // 配合 class-validator 的可选校验
remark?: string;
}
3.3 装饰器速查表(收藏即用)
| 装饰器 | 用在哪 | 作用 |
|---|---|---|
| @ApiTags('xxx') | Controller 类 | 接口分组(Swagger 里按组显示) |
| @ApiOperation({ summary }) | 接口方法 | 接口简短说明 |
| @ApiResponse({ status, description }) | 接口方法 | 响应码说明(成功 / 失败都要写) |
| @ApiParam({ name, description }) | 接口方法 | 路径参数(:id 这类)说明 |
| @ApiQuery({ name, required }) | 接口方法 | 查询参数(?key=val 这类)说明 |
| @ApiProperty({...}) | DTO 属性 | 必选参数的文档说明 |
| @ApiPropertyOptional({...}) | DTO 属性 | 可选参数的文档说明 |
4. 完整实战示例|从 DTO 到 Controller
以 document 模块为例,完整走一遍配置流程,直接抄代码就能用~
4.1 第一步:写 DTO(参数模板)
// src/document/dto/create-document.dto.ts
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsNotEmpty } from 'class-validator';
export class CreateDocumentDto {
@ApiProperty({ description: '文件名', example: '技术文档.pdf' })
@IsString()
@IsNotEmpty()
fileName: string;
@ApiProperty({ description: '文件类型(如 pdf/docx)', example: 'pdf' })
@IsString()
@IsNotEmpty()
fileType: string;
@ApiProperty({ description: '文件大小(单位:KB)', example: '2048' })
@IsString()
@IsNotEmpty()
fileSize: string;
}
4.2 第二步:写 Controller(接口 + 文档)
// src/document/document.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
import { DocumentService } from './document.service';
import { CreateDocumentDto } from './dto/create-document.dto';
@ApiTags('document') // 分组:document
@Controller('document-parse') // 接口前缀:/document-parse
export class DocumentController {
constructor(private readonly documentService: DocumentService) {}
@Post()
@ApiOperation({ summary: '创建文档' }) // 接口说明
@ApiResponse({ status: 201, description: '文档创建成功!' })
@ApiResponse({ status: 400, description: '参数错误,请检查字段格式~' })
create(@Body() createDocumentDto: CreateDocumentDto) {
return this.documentService.create(createDocumentDto);
}
}
4.3 第三步:确认 main.ts 配置(已在第二步配置过)
启动项目后,访问 http://localhost:3000/api,就能看到 document 分组下的接口啦~
5. Swagger UI 测试指南|小白也会用
文档生成后,直接在页面测试接口,不用开 Postman,步骤超简单:
5.1 测试步骤(图文拆解)
- 打开 http://localhost:3000/api,找到要测试的接口(按 Tag 分组查找)
- 点击接口展开,再点击右上角的 Try it out 按钮(进入测试模式)
- 填写参数:如果是 POST 接口,会自动显示 DTO 定义的字段和示例值,直接改就行;路径参数 / 查询参数直接填输入框
- 点击 Execute 按钮(发送请求)
- 查看结果:下方会显示响应体、状态码、响应头,成功失败一目了然~
5.2 额外福利:复制 curl 命令
测试成功后,Swagger 会自动生成对应的 curl 命令,复制到终端就能用,不用自己手写!示例:
curl -X 'POST' \
'http://localhost:3000/document-parse' \
-H 'Content-Type: application/json' \
-d '{
"fileName": "技术文档.pdf",
"fileType": "pdf",
"fileSize": "2048"
}'
5.3 两个实用地址(收藏!)
- Swagger UI 页面:http://localhost:3000/api(测试 + 看文档)
- OpenAPI JSON 地址:http://localhost:3000/api-json(可导入 Postman,一键同步接口)
6. 踩坑记录|5 个高频问题 + 解决方案
这部分是重点!都是我在 ai-knowledge 项目中实际踩过的坑,花了好几个小时排查,大家直接避坑~
坑 1:数据库连接失败 ——SQLite 驱动找不到
报错信息:
DriverPackageNotInstalledError: SQLite package has not been found installed. Please run "npm install sqlite3".
原因:项目用了 pnpm monorepo,sqlite3 包被 pnpm 的严格依赖隔离机制挡住了(pnpm 不允许包访问未声明的依赖)
解决方案:把数据库驱动改成 better-sqlite3(项目已安装,兼容性更好)
// src/database/database.module.ts
TypeOrmModule.forRoot({
type: 'better-sqlite3', // 原来是 'sqlite',改成这个
database: ':memory:', // 内存数据库(按需修改)
entities: [__dirname + '/../**/*.entity{.ts,.js}'],
synchronize: true,
})
坑 2:better-sqlite3 原生二进制未编译
报错信息:
Error: Could not locate the bindings file. Tried: .../better-sqlite3/build/better_sqlite3.node
原因:better-sqlite3 是 C++ 原生模块,安装时需要编译,pnpm 可能跳过了编译步骤
解决方案:手动触发编译
# 进入 better-sqlite3 包目录
cd node_modules/.pnpm/better-sqlite3@12.8.0/node_modules/better-sqlite3
# 执行编译命令
npx prebuild-install
坑 3:timestamp 类型不兼容 better-sqlite3
报错信息:
DataTypeNotSupportedError: Data type "timestamp" in "DocumentParse.createdAt" is not supported by "better-sqlite3" database.
原因:SQLite 没有原生的 timestamp 类型,better-sqlite3 驱动校验更严格
解决方案:Entity 中把 timestamp 改成 datetime
// src/document/entities/document-parse.entity.ts
@CreateDateColumn({ type: 'datetime' }) // 原来是 'timestamp',改成 'datetime'
createdAt: Date;
@UpdateDateColumn({ type: 'datetime' })
updatedAt: Date;
坑 4:ConfigService 找不到
报错信息:
Nest could not find ConfigService element (this provider does not exist in the current context)
原因:main.ts 中用了 app.get(ConfigService),但 AppModule 没导入 ConfigModule
解决方案:在 app.module.ts 中导入 ConfigModule
// src/app.module.ts
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot({
envFilePath: '.env', // 加载 .env 文件(按需配置)
}),
// 其他模块...
],
})
export class AppModule {}
坑 5:POST 请求 500——whitelist 过滤掉所有字段(最隐蔽!)
报错信息:
{"statusCode": 500, "message": "Internal server error"}
底层原因(终端日志可见):
SqliteError: NOT NULL constraint failed: document_parse.fileName
问题分析:
main.ts 中配置了全局校验管道,whitelist: true 会过滤掉没有 class-validator 装饰器的字段 —— 如果 DTO 中只写了 @ApiProperty(Swagger 装饰器),没写 @IsString 这类校验装饰器,参数会被全部过滤,请求体变成空对象 {}, 数据库 NOT NULL 字段报错。
解决方案:DTO 中每个字段必须同时加 Swagger 装饰器 + class-validator 装饰器:
// 正确写法
@ApiProperty({ description: '文件名' }) // Swagger 文档用
@IsString() // class-validator 校验用(防止被过滤)
@IsNotEmpty()
fileName: string;
记忆口诀:@ApiProperty 管文档,@IsString 管校验,两个都要有,缺一个就凉~
附:500 错误排查万能技巧
Nest 默认会隐藏 500 错误的详细信息,排查时按以下步骤来,效率翻倍:
方法 1:看终端日志
启动项目的终端会打印完整的错误堆栈,大部分问题看日志就能定位。
方法 2:临时加 try-catch
在 Controller/Service 中加 try-catch,手动返回错误信息(仅开发环境用,上线前删掉):
@Post()
async create(@Body() dto: CreateDocumentDto) {
try {
return await this.service.create(dto);
} catch (error) {
// 临时返回错误详情
return {
message: '接口报错啦~',
error: error.message,
stack: error.stack
};
}
}
方法 3:关闭开发环境错误隐藏
在 main.ts 中配置全局异常过滤器,开发环境返回完整错误(生产环境禁用!):
import { HttpException, HttpStatus } from '@nestjs/common';
app.useGlobalFilters({
catch(exception, host) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status = exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
// 开发环境返回详细错误
if (process.env.NODE_ENV === 'development') {
response.status(status).json({
statusCode: status,
message: exception.message,
stack: exception.stack,
});
} else {
// 生产环境返回通用提示
response.status(status).json({
statusCode: status,
message: '服务器内部错误',
});
}
},
});
最后总结
Swagger 真的是 NestJS 开发的必备工具,配置一次受益全程 —— 自动生成文档、在线测试、同步接口变更,还能减少前后端沟通成本~ 这篇教程的配置和避坑技巧都是实战总结,大家直接抄代码,有问题评论区见呀~
该项目开源github,需要源码的自取即可。
如果觉得有用,记得点赞收藏,下次用 Swagger 时直接翻出来!❤️