关于 api-framework-nestjs
面向使用 TypeScript 构建 Node.js 后端的开发者,介绍 NestJS 的模块化架构与依赖注入实践。指南涵盖路由、DTO 校验、异常处理及请求生命周期各层的职责划分。
主要功能
- 01按功能组织模块
- 02控制器与服务分层
- 03DTO 与声明式请求校验
- 04依赖注入与自定义提供者
- 05守卫、拦截器、管道和过滤器
适用场景
- 01构建结构清晰的 REST API
- 02为请求体和参数定义校验规则
- 03实现认证与基于角色的访问控制
- 04编排日志、响应转换和异常处理
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agents-inc/ skills --skill api-framework-nestjs安装后在 Claude Code 中输入/api-framework-nestjs确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
metadata.yamlreference.mdexamples/advanced.mdauth.mdcore.mddatabase.mdtesting.md详细介绍
NestJS 模式
**快速指南:**NestJS 是一个基于 TypeScript 构建、具有明确约定的模块化 Node.js 框架。使用模块组织功能,使用控制器处理 HTTP 路由,使用服务配合依赖注入处理业务逻辑,使用带有 class-validator 的 DTO 进行验证,使用守卫进行身份验证,使用异常筛选器处理错误。需要特别注意:始终在模块的
providers中注册服务,始终全局启用ValidationPipe并设置whitelist: true,绝不要将业务逻辑放在控制器中,也绝不要使用new实例化服务。NestJS 11 是当前稳定版本(可选择启用 SWC 编译器、Express v5,终止钩子顺序已反转)。
<critical_requirements>
重要:使用此技能前
(每个服务都必须使用 @Injectable(),并将其注册在模块的 providers 数组中)
(必须全局启用 ValidationPipe,并设置 whitelist: true 和 forbidNonWhitelisted: true)
(所有请求正文验证都必须使用带有 class-validator 装饰器的 DTO——绝不要在控制器中手动验证)
(必须抛出 NestJS 内置 HTTP 异常(NotFoundException、BadRequestException 等)——绝不要直接发送状态码)
(依赖项必须使用构造函数注入——绝不要使用 new 手动实例化服务)
</critical_requirements>
**自动检测:**NestJS、@nestjs/common、@nestjs/core、@Module、@Controller、@Injectable、@Get、@Post、@Body、@Param、@Query、@UseGuards、@UseInterceptors、@UsePipes、@UseFilters、CanActivate、NestInterceptor、PipeTransform、ExceptionFilter、ValidationPipe、class-validator、class-transformer
适用场景:
- 使用 TypeScript 和依赖注入构建结构清晰的后端 API
- 需要采用模块化架构并明确划分职责的应用程序
- 需要声明式验证、身份验证和基于角色的访问控制的 REST API
- 需要使用守卫/拦截器/管道/筛选器请求生命周期的项目
不适用场景:
- 不需要框架的简单脚本或无服务器函数
- 仅使用 Express/Fastify 就足够的项目(不需要 DI 或模块)
- 前端代码
详细资源:
- examples/core.md — 功能模块、CRUD、DTO、动态模块、异常筛选器、自定义提供者
- examples/database.md — 数据库集成和事务的 NestJS DI 模式
- examples/auth.md — Passport.js 集成、JWT 策略、身份验证守卫、RBAC
- examples/testing.md — 使用
Test.createTestingModule进行单元测试,使用 supertest 进行端到端测试 - examples/advanced.md — 拦截器、自定义管道、自定义装饰器、配置、CQRS、Swagger
- reference.md — CLI 命令、项目结构、装饰器表、决策框架
## 设计理念
NestJS 采用受 Angular 启发的模块化、由装饰器驱动的架构。每项功能都组织在模块中,模块包含控制器(HTTP 层)、服务(业务逻辑)和支持性基础设施(守卫、管道、拦截器、过滤器)。
核心原则:
- 模块化——将相关的控制器、服务和提供者归入功能模块。模块是主要的组织单元。
- 依赖注入——切勿手动实例化服务。将服务声明为
@Injectable(),并通过构造函数注入让 NestJS 解析依赖关系图。 - 由装饰器驱动——装饰器(
@Controller、@Get、@Body、@UseGuards)会附加元数据,NestJS 使用这些元数据构建路由、验证和中间件管道。 - 关注点分离——控制器处理 HTTP 请求/响应。服务处理业务逻辑。守卫处理授权。管道处理验证/转换。过滤器处理异常。
- 约定优于配置——遵循 NestJS 约定(每项功能使用一个模块、每个资源使用一个控制器、使用 DTO 进行验证),即可获得开箱即用的功能。
关键模式
模块系统
每个 NestJS 应用都有一个根 AppModule,用于导入功能模块。每个功能模块都包含其控制器、服务和提供者。导出其他模块需要使用的服务。
// Feature module — one per resource
@Module({
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService], // Available to other modules
})
export class UsersModule {}
优点: 每项功能都有自己的封装;通过导入/导出显式定义依赖关系图;可单独测试
完整的 CRUD 模块、动态模块和自定义提供者示例,请参见 examples/core.md。
控制器——精简的路由层
控制器只应提取请求数据并将其委托给服务,不应包含业务逻辑。
@Controller("users")
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get(":id")
findOne(@Param("id", ParseIntPipe) id: number) {
return this.usersService.findOne(id);
}
@Post()
@HttpCode(HttpStatus.CREATED)
create(@Body() dto: CreateUserDto) {
return this.usersService.create(dto);
}
}
优点: ParseIntPipe 可验证并转换参数;使用 @HttpCode 显式设置状态码;将处理精简地委托给服务
反模式: 在控制器中编写业务逻辑、手动验证或访问数据库——始终将这些工作委托给服务。
使用 class-validator 的 DTO
使用带有 class-validator 装饰器的 DTO 验证所有请求。全局启用 ValidationPipe。
const MIN_PASSWORD_LENGTH = 8;
export class CreateUserDto {
@IsEmail()
email: string;
@IsString()
@MinLength(MIN_PASSWORD_LENGTH)
password: string;
}
// Update DTO — reuses validation rules
export class UpdateUserDto extends PartialType(CreateUserDto) {}
// main.ts — Enable globally
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // Strip unknown properties
forbidNonWhitelisted: true, // Reject unknown properties
transform: true, // Auto-transform to DTO instances
}),
);
优点: 声明式验证;whitelist 可防止批量赋值;PartialType 可避免重复规则
嵌套 DTO、带分页的查询 DTO 以及验证组示例,请参见 examples/core.md。
服务与依赖注入
服务包含业务逻辑。使用 @Injectable() 装饰,并通过构造函数注入。
@Injectable()
export class UsersService {
findOne(id: number): User {
const user = this.users.find((u) => u.id === id);
if (!user) {
throw new NotFoundException(`User with id ${id} not found`);
}
return user;
}
}
优点: @Injectable() 启用依赖注入;抛出 NestJS HTTP 异常;业务逻辑纯粹,不涉及 HTTP
自定义提供者
对非类提供者(工厂、值、类提供者)使用基于令牌的注入:
const DATABASE_CONNECTION = "DATABASE_CONNECTION";
const databaseProvider = {
provide: DATABASE_CONNECTION,
useFactory: async (configService: ConfigService) => {
return createConnection(configService.get("database"));
},
inject: [ConfigService],
};
// Inject with @Inject token
constructor(@Inject(DATABASE_CONNECTION) private readonly db: Connection) {}
完整的提供者示例,请参见 examples/core.md。
异常处理
在服务中抛出 NestJS 内置 HTTP 异常。使用异常过滤器自定义错误响应格式。
// Service — throw built-in exceptions
throw new NotFoundException("Resource not found");
throw new ConflictException("Resource already exists");
throw new BadRequestException("Invalid input");
throw new UnauthorizedException("Authentication required");
要点: NestJS 会自动将这些异常转换为状态码正确的 HTTP 响应。切勿直接发送原始状态码。
如需自定义错误响应结构,请使用全局 @Catch() 异常过滤器。参见 examples/core.md。
守卫与中间件
守卫决定是否允许请求继续(授权)。中间件在路由之前运行(日志记录、CORS)。
// Guard — implements CanActivate
@Injectable()
export class JwtAuthGuard implements CanActivate {
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest<Request>();
// Validate token, attach user to request
return true;
}
}
// Apply to routes
@UseGuards(JwtAuthGuard, RolesGuard)
@Controller("admin")
export class AdminController {}
优点: 守卫可注入(可以使用服务)、可组合(按顺序运行),并可使用 Reflector 实现基于元数据的访问控制
JWT 身份验证、Passport.js 集成、RBAC 和 @Public() 装饰器示例,请参见 examples/auth.md。
拦截器
拦截器会包装处理程序的执行过程,用于处理横切关注点(响应封装、日志记录、缓存)。
@Injectable()
export class TransformResponseInterceptor<T> implements NestInterceptor {
intercept(
context: ExecutionContext,
next: CallHandler,
): Observable<ApiResponse<T>> {
return next.handle().pipe(
map((data) => ({
success: true,
data,
timestamp: new Date().toISOString(),
})),
);
}
}
日志记录、缓存和自定义管道模式示例,请参见 examples/advanced.md。
<decision_framework>
决策框架
请求生命周期
Incoming Request
→ Middleware (logging, CORS, body parsing)
→ Guards (authentication, authorization)
→ Interceptors (pre-handler: transform request, start timing)
→ Pipes (validation, transformation)
→ Route Handler (controller method)
→ Interceptors (post-handler: transform response, log timing)
→ Exception Filters (catch and format errors)
→ Response
</decision_framework>
应使用哪一层
Need to process raw request before routing?
├─ YES → Middleware (logging, CORS, rate limiting)
└─ NO → Does it decide allow/deny for a route?
├─ YES → Guard (auth, roles, permissions)
└─ NO → Does it transform/validate input data?
├─ YES → Pipe (validation, type coercion)
└─ NO → Does it wrap handler execution?
├─ YES → Interceptor (timing, caching, response mapping)
└─ NO → Does it handle errors?
├─ YES → Exception Filter
└─ NO → Put it in the service layer
模块组织
Is this a cross-cutting concern (auth, config, logging)?
├─ YES → Global module or shared module
└─ NO → Is it a business feature (users, orders, products)?
├─ YES → Feature module (users.module.ts)
└─ NO → Is it infrastructure (database, cache, queue)?
├─ YES → Infrastructure module
└─ NO → Part of the closest feature module
</decision_framework>
<red_flags>
危险信号
高优先级问题:
- 将业务逻辑放在控制器中,而不是服务中
- 服务缺少
@Injectable()(依赖注入会在运行时静默失败) - 未全局启用
ValidationPipe(DTO 不会经过验证) - 请求体使用
any,而不是类型化 DTO - 使用
new实例化服务,而不是通过构造函数注入 - 抛出原始
Error,而不是 NestJS HTTP 异常(会导致返回 500,而非正确的状态码)
中优先级问题:
- 未从模块中导出服务(其他模块无法导入它们)
- 只需要一个服务,却导入整个模块
ValidationPipe缺少whitelist: true(存在批量赋值漏洞)- 在流式处理场景之外使用
@Res()装饰器(会退出 NestJS 的响应处理) - 更新 DTO 未使用
PartialType/PickType/OmitType(验证逻辑重复)
常见错误:
- 模块依赖循环——使用
forwardRef()重构,或提取共享逻辑 - 忘记在模块中注册提供者——服务注入会在运行时失败
- 对异步操作使用同步守卫——应返回
Promise<boolean>或Observable<boolean> - 自定义过滤器未处理所有异常类型——应始终为未知错误设置兜底处理
注意事项和边缘情况:
@UseGuards(AuthGuard)接受类引用,而不是实例——NestJS 会通过依赖注入实例化它ValidationPipe设置transform: true后,会自动将查询参数转换为其声明的类型- 守卫在中间件之后、拦截器和管道之前执行
- 不带参数的
@Catch()会捕获所有异常,而不仅仅是 HttpException IntrinsicException(NestJS 11)抛出异常时不会由框架自动记录日志——适用于预期的流程控制- NestJS 11:终止生命周期钩子(
OnModuleDestroy、OnApplicationShutdown)现在会按相反顺序执行 - NestJS 11:Express v5 要求使用带名称的通配符(
/*splat,而不是/*) - NestJS 11:SWC 是受支持的可选编译器,可通过
nest-cli.json配置("builder": "swc")——构建速度比 tsc 快 20 倍 - NestJS 11:
ParseDatePipe现已内置——无需自定义日期解析管道 - 请求作用域提供者(
Scope.REQUEST)会影响性能——仅在需要时使用 forwardRef()应作为最后手段——循环依赖通常意味着设计存在问题
</red_flags>
<critical_reminders>
重要提醒
(必须在每个服务上使用 @Injectable(),并将其注册到模块的 providers 数组中)
(必须全局启用 ValidationPipe,并设置 whitelist: true 和 forbidNonWhitelisted: true)
(必须使用带有 class-validator 装饰器的 DTO 验证所有请求体——绝不要在控制器中手动验证)
(必须抛出 NestJS 内置 HTTP 异常(NotFoundException、BadRequestException 等)——绝不要发送原始状态码)
(必须使用构造函数注入依赖——绝不要使用 new 手动实例化服务)
若不遵守这些规则,将生成未经验证、无法测试且依赖注入失效的 NestJS 代码。
</critical_reminders>