nestjs-best-practices
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseNestJS Best Practices
NestJS最佳实践
Overview
概述
This skill provides a curated set of best practices for building production-grade NestJS applications. All guidelines are grounded in the Official NestJS Documentation and enforce consistent, maintainable, and scalable patterns.
本技能为构建生产级NestJS应用提供了一套精心整理的最佳实践。所有准则均基于NestJS官方文档,旨在强化一致、可维护且可扩展的代码模式。
When to Use
适用场景
- Designing or refactoring NestJS module architecture
- Implementing dependency injection with custom or scoped providers
- Creating exception filters and standardizing error responses
- Validating DTOs with and
class-validatorValidationPipe - Integrating Drizzle ORM within NestJS providers
- Reviewing NestJS code for architectural anti-patterns
- Onboarding new developers to a NestJS codebase
- 设计或重构NestJS模块架构
- 实现带有自定义或作用域提供者的依赖注入
- 创建异常过滤器并标准化错误响应
- 使用和
class-validator验证DTOValidationPipe - 在NestJS提供者中集成Drizzle ORM
- 审查NestJS代码中的架构反模式
- 指导新开发者熟悉NestJS代码库
Instructions
操作指南
1. Modular Architecture
1. 模块化架构
Follow strict module encapsulation. Each domain feature should be its own :
@Module()- Export only what other modules need — keep internal providers private
- Use only as a last resort for circular dependencies; prefer restructuring
forwardRef() - Group related controllers, services, and repositories within the same module
- Use a for cross-cutting concerns (logging, configuration, caching)
SharedModule
See for enforcement rules.
references/arch-module-boundaries.md严格遵循模块封装原则。每个领域功能应对应独立的:
@Module()- 仅导出其他模块所需的内容——内部提供者保持私有
- 仅在万不得已时使用处理循环依赖;优先重构代码结构
forwardRef() - 将相关的控制器、服务和仓储归类到同一模块中
- 使用处理横切关注点(日志、配置、缓存)
SharedModule
有关实施规则,请参阅。
references/arch-module-boundaries.md2. Dependency Injection
2. 依赖注入
Choose the correct provider scope based on use case:
| Scope | Lifecycle | Use Case |
|---|---|---|
| Singleton (shared) | Stateless services, repositories |
| Per-request instance | Request-scoped data (tenant, user context) |
| New instance per injection | Stateful utilities, per-consumer caches |
- Default to scope — only use
DEFAULTorREQUESTwhen justifiedTRANSIENT - Use constructor injection exclusively — avoid property injection
- Register custom providers with ,
useClass,useValue, oruseFactoryuseExisting
See for enforcement rules.
references/di-provider-scoping.md根据使用场景选择正确的提供者作用域:
| 作用域 | 生命周期 | 适用场景 |
|---|---|---|
| 单例(共享实例) | 无状态服务、仓储类 |
| 每个请求对应一个实例 | 请求作用域数据(租户、用户上下文) |
| 每次注入生成新实例 | 有状态工具类、每个消费者独立的缓存 |
- 默认使用作用域——仅在有合理理由时才使用
DEFAULT或REQUESTTRANSIENT - 仅使用构造函数注入——避免属性注入
- 使用、
useClass、useValue或useFactory注册自定义提供者useExisting
有关实施规则,请参阅。
references/di-provider-scoping.md3. Request Lifecycle
3. 请求生命周期
Understand and respect the NestJS request processing pipeline:
Middleware → Guards → Interceptors (before) → Pipes → Route Handler → Interceptors (after) → Exception Filters- Middleware: Cross-cutting concerns (logging, CORS, body parsing)
- Guards: Authorization and authentication checks (return /
true)false - Interceptors: Transform response data, add caching, measure timing
- Pipes: Validate and transform input parameters
- Exception Filters: Catch and format error responses
理解并遵循NestJS请求处理流程:
中间件 → 守卫 → 拦截器(前置处理) → 管道 → 路由处理器 → 拦截器(后置处理) → 异常过滤器- 中间件:处理横切关注点(日志、CORS、请求体解析)
- 守卫:授权与身份验证检查(返回/
true)false - 拦截器:转换响应数据、添加缓存、统计耗时
- 管道:验证并转换输入参数
- 异常过滤器:捕获并格式化错误响应
4. Error Handling
4. 错误处理
Standardize error responses across the application:
- Extend for HTTP-specific errors
HttpException - Create domain-specific exception classes (e.g., )
OrderNotFoundException - Implement a global for consistent error formatting
ExceptionFilter - Use the Result pattern for expected business logic failures
- Never silently swallow exceptions
See for enforcement rules.
references/error-exception-filters.md在应用中标准化错误响应:
- 针对HTTP特定错误扩展
HttpException - 创建领域特定的异常类(例如:)
OrderNotFoundException - 实现全局以确保错误格式一致
ExceptionFilter - 对预期的业务逻辑失败使用结果模式(Result pattern)
- 绝对不要静默吞掉异常
有关实施规则,请参阅。
references/error-exception-filters.md5. Validation
5. 验证
Enforce input validation at the API boundary:
- Enable globally with
ValidationPipeandtransform: truewhitelist: true - Decorate all DTO properties with decorators
class-validator - Use for type coercion (
class-transformer,@Type())@Transform() - Create separate DTOs for Create, Update, and Response operations
- Never trust raw user input — validate everything
See for enforcement rules.
references/api-validation-dto.md在API边界强制执行输入验证:
- 全局启用并设置
ValidationPipe和transform: truewhitelist: true - 使用装饰器标记所有DTO属性
class-validator - 使用进行类型转换(
class-transformer、@Type())@Transform() - 为创建、更新和响应操作分别定义独立的DTO
- 绝不信任原始用户输入——所有内容都要验证
有关实施规则,请参阅。
references/api-validation-dto.md6. Database Patterns (Drizzle ORM)
6. 数据库模式(Drizzle ORM)
Integrate Drizzle ORM following NestJS provider conventions:
- Wrap the Drizzle client in an injectable provider
- Use the Repository pattern for data access encapsulation
- Define schemas in dedicated schema files per domain module
- Use transactions for multi-step operations
- Keep database logic out of controllers
See for enforcement rules.
references/db-drizzle-patterns.md遵循NestJS提供者约定集成Drizzle ORM:
- 将Drizzle客户端包装为可注入的提供者
- 使用仓储模式封装数据访问逻辑
- 为每个领域模块在专用的Schema文件中定义数据库模式
- 对多步骤操作使用事务
- 避免在控制器中编写数据库逻辑
有关实施规则,请参阅。
references/db-drizzle-patterns.mdBest Practices
最佳实践对比
| Area | Do | Don't |
|---|---|---|
| Modules | One module per domain feature | Dump everything in |
| DI Scoping | Default to singleton scope | Use |
| Error Handling | Custom exception filters + domain errors | Bare |
| Validation | Global | Manual |
| Database | Repository pattern with injected client | Direct DB queries in controllers |
| Testing | Unit test services, e2e test controllers | Skip tests or test implementation details |
| Configuration | | Hardcode values or use |
| 领域 | 推荐做法 | 不推荐做法 |
|---|---|---|
| 模块设计 | 每个领域功能对应一个模块 | 将所有内容都塞进 |
| 依赖注入作用域 | 默认使用单例作用域 | 无合理理由使用 |
| 错误处理 | 自定义异常过滤器 + 领域特定错误 | 仅用 |
| 数据验证 | 全局 | 在控制器中手动写 |
| 数据库操作 | 基于注入客户端的仓储模式 | 在控制器中直接执行数据库查询 |
| 测试 | 单元测试服务,端到端测试控制器 | 跳过测试或测试实现细节 |
| 配置管理 | 使用 | 硬编码值或直接使用 |
Examples
示例
Example 1: Creating a New Domain Module
示例1:创建新的领域模块
When building a new "Product" feature:
typescript
// product/product.module.ts
@Module({
imports: [DatabaseModule],
controllers: [ProductController],
providers: [ProductService, ProductRepository],
exports: [ProductService],
})
export class ProductModule {}构建新的“商品”功能时:
typescript
// product/product.module.ts
@Module({
imports: [DatabaseModule],
controllers: [ProductController],
providers: [ProductService, ProductRepository],
exports: [ProductService],
})
export class ProductModule {}Example 2: DTO with Full Validation
示例2:带有完整验证的DTO
typescript
// product/dto/create-product.dto.ts
import { IsString, IsNumber, IsPositive, MaxLength } from 'class-validator';
export class CreateProductDto {
@IsString()
@MaxLength(255)
readonly name: string;
@IsNumber()
@IsPositive()
readonly price: number;
}typescript
// product/dto/create-product.dto.ts
import { IsString, IsNumber, IsPositive, MaxLength } from 'class-validator';
export class CreateProductDto {
@IsString()
@MaxLength(255)
readonly name: string;
@IsNumber()
@IsPositive()
readonly price: number;
}Example 3: Service with Proper Error Handling
示例3:具备完善错误处理的服务
typescript
@Injectable()
export class ProductService {
constructor(private readonly productRepository: ProductRepository) {}
async findById(id: string): Promise<Product> {
const product = await this.productRepository.findById(id);
if (product === null) {
throw new ProductNotFoundException(id);
}
return product;
}
}typescript
@Injectable()
export class ProductService {
constructor(private readonly productRepository: ProductRepository) {}
async findById(id: string): Promise<Product> {
const product = await this.productRepository.findById(id);
if (product === null) {
throw new ProductNotFoundException(id);
}
return product;
}
}Constraints and Warnings
约束与警告
- Do not mix scopes without justification — -scoped providers cascade to all dependents
REQUEST - Never access database directly from controllers — always go through service and repository layers
- Avoid — restructure modules to eliminate circular dependencies
forwardRef() - Do not skip — always validate at the API boundary with DTOs
ValidationPipe - Never hardcode secrets — use with environment variables
@nestjs/config - Keep modules focused — one domain feature per module, avoid "god modules"
- 无合理理由不要混合使用不同作用域 —— 作用域的提供者会将作用域传递给所有依赖它的对象
REQUEST - 绝对不要在控制器中直接访问数据库 —— 始终通过服务和仓储层操作
- 避免使用—— 重构模块结构以消除循环依赖
forwardRef() - 不要跳过—— 始终在API边界通过DTO进行验证
ValidationPipe - 绝对不要硬编码密钥 —— 使用搭配环境变量
@nestjs/config - 保持模块专注 —— 每个模块对应一个领域功能,避免“上帝模块”
References
参考资料
- — Deep-dive into NestJS architectural patterns
references/architecture.md - — Individual enforcement rules with correct/incorrect examples
references/ - — Starter templates for common NestJS components
assets/templates/
- —— NestJS架构模式深度解析
references/architecture.md - —— 包含正确/错误示例的独立实施规则文档
references/ - —— 常见NestJS组件的启动模板
assets/templates/