nestjs-best-practices

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

NestJS 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
    class-validator
    and
    ValidationPipe
  • Integrating Drizzle ORM within NestJS providers
  • Reviewing NestJS code for architectural anti-patterns
  • Onboarding new developers to a NestJS codebase
  • 设计或重构NestJS模块架构
  • 实现带有自定义或作用域提供者的依赖注入
  • 创建异常过滤器并标准化错误响应
  • 使用
    class-validator
    ValidationPipe
    验证DTO
  • 在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
    forwardRef()
    only as a last resort for circular dependencies; prefer restructuring
  • Group related controllers, services, and repositories within the same module
  • Use a
    SharedModule
    for cross-cutting concerns (logging, configuration, caching)
See
references/arch-module-boundaries.md
for enforcement rules.
严格遵循模块封装原则。每个领域功能应对应独立的
@Module()
  • 仅导出其他模块所需的内容——内部提供者保持私有
  • 仅在万不得已时使用
    forwardRef()
    处理循环依赖;优先重构代码结构
  • 将相关的控制器、服务和仓储归类到同一模块中
  • 使用
    SharedModule
    处理横切关注点(日志、配置、缓存)
有关实施规则,请参阅
references/arch-module-boundaries.md

2. Dependency Injection

2. 依赖注入

Choose the correct provider scope based on use case:
ScopeLifecycleUse Case
DEFAULT
Singleton (shared)Stateless services, repositories
REQUEST
Per-request instanceRequest-scoped data (tenant, user context)
TRANSIENT
New instance per injectionStateful utilities, per-consumer caches
  • Default to
    DEFAULT
    scope — only use
    REQUEST
    or
    TRANSIENT
    when justified
  • Use constructor injection exclusively — avoid property injection
  • Register custom providers with
    useClass
    ,
    useValue
    ,
    useFactory
    , or
    useExisting
See
references/di-provider-scoping.md
for enforcement rules.
根据使用场景选择正确的提供者作用域:
作用域生命周期适用场景
DEFAULT
单例(共享实例)无状态服务、仓储类
REQUEST
每个请求对应一个实例请求作用域数据(租户、用户上下文)
TRANSIENT
每次注入生成新实例有状态工具类、每个消费者独立的缓存
  • 默认使用
    DEFAULT
    作用域——仅在有合理理由时才使用
    REQUEST
    TRANSIENT
  • 仅使用构造函数注入——避免属性注入
  • 使用
    useClass
    useValue
    useFactory
    useExisting
    注册自定义提供者
有关实施规则,请参阅
references/di-provider-scoping.md

3. 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
    HttpException
    for HTTP-specific errors
  • Create domain-specific exception classes (e.g.,
    OrderNotFoundException
    )
  • Implement a global
    ExceptionFilter
    for consistent error formatting
  • Use the Result pattern for expected business logic failures
  • Never silently swallow exceptions
See
references/error-exception-filters.md
for enforcement rules.
在应用中标准化错误响应:
  • 针对HTTP特定错误扩展
    HttpException
  • 创建领域特定的异常类(例如:
    OrderNotFoundException
  • 实现全局
    ExceptionFilter
    以确保错误格式一致
  • 对预期的业务逻辑失败使用结果模式(Result pattern)
  • 绝对不要静默吞掉异常
有关实施规则,请参阅
references/error-exception-filters.md

5. Validation

5. 验证

Enforce input validation at the API boundary:
  • Enable
    ValidationPipe
    globally with
    transform: true
    and
    whitelist: true
  • Decorate all DTO properties with
    class-validator
    decorators
  • Use
    class-transformer
    for type coercion (
    @Type()
    ,
    @Transform()
    )
  • Create separate DTOs for Create, Update, and Response operations
  • Never trust raw user input — validate everything
See
references/api-validation-dto.md
for enforcement rules.
在API边界强制执行输入验证:
  • 全局启用
    ValidationPipe
    并设置
    transform: true
    whitelist: true
  • 使用
    class-validator
    装饰器标记所有DTO属性
  • 使用
    class-transformer
    进行类型转换(
    @Type()
    @Transform()
  • 为创建、更新和响应操作分别定义独立的DTO
  • 绝不信任原始用户输入——所有内容都要验证
有关实施规则,请参阅
references/api-validation-dto.md

6. 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
references/db-drizzle-patterns.md
for enforcement rules.
遵循NestJS提供者约定集成Drizzle ORM:
  • 将Drizzle客户端包装为可注入的提供者
  • 使用仓储模式封装数据访问逻辑
  • 为每个领域模块在专用的Schema文件中定义数据库模式
  • 对多步骤操作使用事务
  • 避免在控制器中编写数据库逻辑
有关实施规则,请参阅
references/db-drizzle-patterns.md

Best Practices

最佳实践对比

AreaDoDon't
ModulesOne module per domain featureDump everything in
AppModule
DI ScopingDefault to singleton scopeUse
REQUEST
scope without justification
Error HandlingCustom exception filters + domain errorsBare
try/catch
with
console.log
ValidationGlobal
ValidationPipe
+ DTO decorators
Manual
if
checks in controllers
DatabaseRepository pattern with injected clientDirect DB queries in controllers
TestingUnit test services, e2e test controllersSkip tests or test implementation details
Configuration
@nestjs/config
with typed schemas
Hardcode values or use
process.env
领域推荐做法不推荐做法
模块设计每个领域功能对应一个模块将所有内容都塞进
AppModule
依赖注入作用域默认使用单例作用域无合理理由使用
REQUEST
作用域
错误处理自定义异常过滤器 + 领域特定错误仅用
try/catch
配合
console.log
数据验证全局
ValidationPipe
+ DTO装饰器
在控制器中手动写
if
判断
数据库操作基于注入客户端的仓储模式在控制器中直接执行数据库查询
测试单元测试服务,端到端测试控制器跳过测试或测试实现细节
配置管理使用
@nestjs/config
搭配类型化Schema
硬编码值或直接使用
process.env

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

约束与警告

  1. Do not mix scopes without justification
    REQUEST
    -scoped providers cascade to all dependents
  2. Never access database directly from controllers — always go through service and repository layers
  3. Avoid
    forwardRef()
    — restructure modules to eliminate circular dependencies
  4. Do not skip
    ValidationPipe
    — always validate at the API boundary with DTOs
  5. Never hardcode secrets — use
    @nestjs/config
    with environment variables
  6. Keep modules focused — one domain feature per module, avoid "god modules"
  1. 无合理理由不要混合使用不同作用域 ——
    REQUEST
    作用域的提供者会将作用域传递给所有依赖它的对象
  2. 绝对不要在控制器中直接访问数据库 —— 始终通过服务和仓储层操作
  3. 避免使用
    forwardRef()
    —— 重构模块结构以消除循环依赖
  4. 不要跳过
    ValidationPipe
    —— 始终在API边界通过DTO进行验证
  5. 绝对不要硬编码密钥 —— 使用
    @nestjs/config
    搭配环境变量
  6. 保持模块专注 —— 每个模块对应一个领域功能,避免“上帝模块”

References

参考资料

  • references/architecture.md
    — Deep-dive into NestJS architectural patterns
  • references/
    — Individual enforcement rules with correct/incorrect examples
  • assets/templates/
    — Starter templates for common NestJS components
  • references/architecture.md
    —— NestJS架构模式深度解析
  • references/
    —— 包含正确/错误示例的独立实施规则文档
  • assets/templates/
    —— 常见NestJS组件的启动模板