nestjs-best-practices

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

NestJS 11 Best Practices

NestJS 11 最佳实践

Quick Reference

快速参考

TopicWhen to UseReference
Core ArchitectureModules, Providers, DI, forwardRef, custom decoratorscore-architecture.md
Request LifecycleMiddleware, Guards, Interceptors, Pipes, Filtersrequest-lifecycle.md
Validation & PipesDTOs, class-validator, ValidationPipe, transformsvalidation-pipes.md
AuthenticationJWT, Passport, Guards, Local/OAuth strategies, RBACauthentication.md
DatabaseTypeORM, Prisma, Drizzle ORM, repository patternsdatabase-integration.md
TestingUnit tests, E2E tests, mocking providerstesting.md
OpenAPI & GraphQLSwagger decorators, resolvers, subscriptionsopenapi-graphql.md
MicroservicesTCP, Redis, NATS, Kafka patternsmicroservices.md
主题使用场景参考文档
核心架构模块、提供者、依赖注入、forwardRef、自定义装饰器core-architecture.md
请求生命周期中间件、守卫、拦截器、管道、过滤器request-lifecycle.md
验证与管道DTO、class-validator、ValidationPipe、转换逻辑validation-pipes.md
身份认证JWT、Passport、守卫、本地/OAuth策略、RBACauthentication.md
数据库集成TypeORM、Prisma、Drizzle ORM、仓库模式database-integration.md
测试单元测试、E2E测试、提供者模拟testing.md
OpenAPI与GraphQLSwagger装饰器、解析器、订阅openapi-graphql.md
微服务TCP、Redis、NATS、Kafka模式microservices.md

Essential Patterns

核心模式

Module with Providers

包含提供者的模块

typescript
@Module({
  imports: [DatabaseModule],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService], // Export for other modules
})
export class UsersModule {}
typescript
@Module({
  imports: [DatabaseModule],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService], // Export for other modules
})
export class UsersModule {}

Controller with Validation

带验证的控制器

typescript
@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

  @Get(':id')
  findOne(@Param('id', ParseIntPipe) id: number) {
    return this.usersService.findOne(id);
  }
}
typescript
@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

  @Get(':id')
  findOne(@Param('id', ParseIntPipe) id: number) {
    return this.usersService.findOne(id);
  }
}

DTO with Validation

带验证的DTO

typescript
import { IsEmail, IsString, MinLength, IsOptional } from 'class-validator';

export class CreateUserDto {
  @IsEmail()
  email: string;

  @IsString()
  @MinLength(8)
  password: string;

  @IsOptional()
  @IsString()
  name?: string;
}
typescript
import { IsEmail, IsString, MinLength, IsOptional } from 'class-validator';

export class CreateUserDto {
  @IsEmail()
  email: string;

  @IsString()
  @MinLength(8)
  password: string;

  @IsOptional()
  @IsString()
  name?: string;
}

Exception Filter

异常过滤器

typescript
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const status = exception.getStatus();

    response.status(status).json({
      statusCode: status,
      message: exception.message,
      timestamp: new Date().toISOString(),
    });
  }
}
typescript
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const status = exception.getStatus();

    response.status(status).json({
      statusCode: status,
      message: exception.message,
      timestamp: new Date().toISOString(),
    });
  }
}

Guard with JWT

基于JWT的守卫

typescript
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  canActivate(context: ExecutionContext) {
    return super.canActivate(context);
  }
}
typescript
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  canActivate(context: ExecutionContext) {
    return super.canActivate(context);
  }
}

NestJS 11 Breaking Changes

NestJS 11 破坏性变更

  • Express v5: Wildcards must be named (e.g.,
    *splat
    ), optional params use braces
    /:file{.:ext}
  • Node.js 20+: Minimum required version
  • Fastify v5: Updated adapter for Fastify users
  • Dynamic Modules: Same module with identical config imported multiple times = separate instances
  • Express v5:通配符必须命名(例如
    *splat
    ),可选参数使用大括号
    /:file{.:ext}
  • Node.js 20+:最低要求版本
  • Fastify v5:为Fastify用户更新了适配器
  • 动态模块:多次导入配置相同的同一模块会生成独立实例

Common Mistakes

常见错误

  1. Not using
    forwardRef()
    for circular deps
    - Causes "cannot resolve dependency" errors; wrap in
    forwardRef(() => ModuleName)
  2. Throwing plain errors instead of HttpException - Loses status codes, breaks exception filters; use
    throw new BadRequestException('message')
  3. Missing
    @Injectable()
    decorator
    - Provider won't be injectable; always decorate services
  4. Global ValidationPipe without
    whitelist: true
    - Allows unexpected properties; set
    whitelist: true, forbidNonWhitelisted: true
  5. Importing modules instead of exporting providers - Use
    exports
    array to share providers across modules
  6. Async config without
    ConfigModule.forRoot()
    - ConfigService undefined; import ConfigModule in AppModule
  7. Testing without
    overrideProvider()
    - Uses real services in unit tests; mock dependencies with
    overrideProvider(Service).useValue(mock)
  8. E2E tests sharing database state - No isolation between tests; use transactions or truncate tables in beforeEach
  1. 未使用
    forwardRef()
    处理循环依赖
    - 会导致“无法解析依赖”错误;需用
    forwardRef(() => ModuleName)
    包裹
  2. 抛出普通错误而非HttpException - 丢失状态码,破坏异常过滤器;请使用
    throw new BadRequestException('message')
  3. 缺少
    @Injectable()
    装饰器
    - 提供者无法被注入;务必为服务添加该装饰器
  4. 全局ValidationPipe未设置
    whitelist: true
    - 允许意外属性传入;需设置
    whitelist: true, forbidNonWhitelisted: true
  5. 导入模块而非导出提供者 - 使用
    exports
    数组在模块间共享提供者
  6. 异步配置未使用
    ConfigModule.forRoot()
    - 会导致ConfigService未定义;在AppModule中导入ConfigModule
  7. 测试时未使用
    overrideProvider()
    - 单元测试中使用真实服务;用
    overrideProvider(Service).useValue(mock)
    模拟依赖
  8. E2E测试共享数据库状态 - 测试间无隔离;在beforeEach中使用事务或清空表数据