NestJS 全面深度指南:从入门到企业级架构实战

2 阅读15分钟

一篇覆盖官方文档全部内容、由浅入深、图文并茂的完整教程


目录

  1. 什么是 NestJS?
  2. 核心架构思想
  3. 快速上手:5分钟启动第一个项目
  4. 核心构建块详解
    • Controllers(控制器)
    • Providers(提供者)
    • Modules(模块)
  5. 请求处理管道
    • Middleware(中间件)
    • Pipes(管道)
    • Guards(守卫)
    • Interceptors(拦截器)
    • Exception Filters(异常过滤器)
  6. 依赖注入进阶
  7. 数据库集成实战
  8. 安全:认证与授权
  9. 配置管理
  10. 微服务架构
  11. 测试策略
  12. 进阶技巧与最佳实践
  13. 完整项目实战示例

1. 什么是 NestJS?

NestJS 是一个用于构建高效、可扩展 Node.js 服务端应用的渐进式框架。它使用 TypeScript 构建,融合了 OOP(面向对象)、FP(函数式)、FRP(响应式)三种编程思想。

最重要的是,它在 Express/Fastify 之上提供了一套强大的架构层,这正是 Node.js 生态长期以来所缺失的东西。

NestJS 哲学:
  架构 = 可测试性 + 可扩展性 + 低耦合 + 易维护

技术栈全景

┌──────────────────────────────────────────────────────────┐
│                      NestJS 框架                          │
│                                                          │
│  TypeScript  +  Decorators  +  DI Container              │
│                                                          │
├──────────────────────────────────────────────────────────┤
│               HTTP 适配层(可选)                          │
│        Express (默认)  |  Fastify (高性能)                │
├──────────────────────────────────────────────────────────┤
│                    Node.js 运行时                         │
└──────────────────────────────────────────────────────────┘

对比其他框架:

框架语言架构约束学习曲线企业级
ExpressJS/TS需自建
FastifyJS/TS需自建
NestJSTS强(Angular风格)开箱即用
Spring BootJava

2. 核心架构思想

NestJS 的架构灵感来源于 Angular,它解决了以下关键问题:在 Node.js 项目中,如何强制实施一套清晰、可维护的模块化架构。

整体架构图

┌─────────────────────────────────────────────────────────────────┐
│                         Client Request                          │
└──────────────────────────────┬──────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                      Middleware Layer                           │
│            (日志、CORS、Body解析、Session 等)                    │
└──────────────────────────────┬──────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                       Guards Layer                              │
│                  (认证、授权、权限检查)                           │
└──────────────────────────────┬──────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                    Interceptors (Pre)                           │
│             (日志记录、请求转换、缓存检查)                        │
└──────────────────────────────┬──────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                       Pipes Layer                               │
│               (数据验证、类型转换、数据清洗)                      │
└──────────────────────────────┬──────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                     Controller Layer                            │
│              (路由匹配、请求处理、响应返回)                       │
│                                                                 │
│    ┌─────────────────────────────────────────────────────┐     │
│    │                   Service Layer                      │     │
│    │           (业务逻辑、数据处理、外部调用)               │     │
│    │                                                      │     │
│    │    ┌──────────────────────────────────────────┐      │     │
│    │    │            Repository Layer               │      │     │
│    │    │         (数据库操作、数据持久化)            │      │     │
│    │    └──────────────────────────────────────────┘      │     │
│    └─────────────────────────────────────────────────────┘     │
└──────────────────────────────┬──────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                   Interceptors (Post)                           │
│              (响应转换、错误映射、日志完成)                       │
└──────────────────────────────┬──────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                   Exception Filters                             │
│              (统一错误处理、错误格式化)                           │
└──────────────────────────────┬──────────────────────────────────┘
                               │
                               ▼
                         Client Response

记忆要点:请求进入时的处理顺序

Middleware → Guards → Interceptors(前) → Pipes → Controller/Handler
         ← Interceptors(后) ← Exception Filters ← (异常发生时)

3. 快速上手

安装与创建项目

# 全局安装 NestJS CLI
npm i -g @nestjs/cli

# 创建新项目
nest new my-project

# 进入目录并启动
cd my-project
npm run start:dev

访问 http://localhost:3000,你会看到 Hello World!

项目初始结构

src/
├── app.controller.ts      # 根控制器(处理 HTTP 请求)
├── app.controller.spec.ts # 控制器单元测试
├── app.module.ts          # 根模块(应用入口)
├── app.service.ts         # 根服务(业务逻辑)
└── main.ts                # 启动文件

main.ts — 应用启动点:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

常用 CLI 命令速查

nest g module    users        # 生成模块
nest g controller users       # 生成控制器
nest g service   users        # 生成服务
nest g resource  users        # 🚀 一键生成完整 CRUD(推荐!)
nest g pipe      validation   # 生成管道
nest g guard     auth         # 生成守卫
nest g interceptor logging    # 生成拦截器
nest g filter    http-exception # 生成异常过滤器

4. 核心构建块详解

4.1 Controllers(控制器)

控制器负责接收 HTTP 请求并返回响应,是应用的入口层。

HTTP 请求
    │
    ▼
┌──────────────────────────────────┐
│         @Controller('cats')       │
│                                  │
│  @Get()         findAll()        │  GET /cats
│  @Get(':id')    findOne()        │  GET /cats/1@Post()        create()         │  POST /cats
│  @Put(':id')    update()         │  PUT /cats/1@Delete(':id') remove()         │  DELETE /cats/1
└──────────────────────────────────┘

简单示例

import { Controller, Get, Post, Body, Param, Delete, Put } from '@nestjs/common';

@Controller('cats')  // 路由前缀:/cats
export class CatsController {
  
  @Get()
  findAll(): string {
    return 'This returns all cats';
  }

  @Get(':id')
  findOne(@Param('id') id: string): string {
    return `This returns cat #${id}`;
  }

  @Post()
  create(@Body() createCatDto: CreateCatDto) {
    return 'This creates a new cat';
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
    return `This updates cat #${id}`;
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return `This removes cat #${id}`;
  }
}

常用参数装饰器对照表

装饰器对应 Express 属性用途
@Req()req完整请求对象
@Res()res完整响应对象
@Param('id')req.params.id路径参数
@Body()req.body请求体
@Query('page')req.query.page查询参数
@Headers('auth')req.headers.auth请求头
@Ip()req.ip客户端 IP

进阶:HTTP 装饰器

@Controller('cats')
export class CatsController {
  
  @Post()
  @HttpCode(204)          // 自定义状态码
  @Header('Cache-Control', 'no-store')  // 自定义响应头
  create() {
    return 'Created';
  }

  @Get('docs')
  @Redirect('https://docs.nestjs.com', 302)  // 重定向
  getDocs(@Query('version') version: string) {
    if (version === '5') {
      return { url: 'https://docs.nestjs.com/v5/' };  // 动态重定向
    }
  }
}

4.2 Providers(提供者)

提供者是 NestJS 中最核心的概念之一。任何带有 @Injectable() 装饰器的类都可以成为提供者(服务、仓库、工厂等),并通过依赖注入系统来使用。

提供者类型:

@Injectable()           @Injectable()           @Injectable()
class CatsService {}    class CatsRepository {}  class EmailService {}
        │                       │                       │
        └───────────────────────┴───────────────────────┘
                                │
                    NestJS IoC Container(控制反转容器)
                                │
                    ┌───────────┴───────────┐
                    │   自动管理实例创建     │
                    │   默认单例模式共享     │
                    │   自动解析依赖关系     │
                    └───────────────────────┘

创建服务(最常见的提供者)

// cats.service.ts
import { Injectable } from '@nestjs/common';

export interface Cat {
  name: string;
  age: number;
  breed: string;
}

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];

  create(cat: Cat) {
    this.cats.push(cat);
  }

  findAll(): Cat[] {
    return this.cats;
  }

  findOne(id: number): Cat {
    return this.cats[id];
  }
}

在控制器中注入服务

// cats.controller.ts
@Controller('cats')
export class CatsController {
  // 通过构造函数注入 ✅ 推荐方式
  constructor(private readonly catsService: CatsService) {}

  @Get()
  findAll(): Cat[] {
    return this.catsService.findAll();  // 调用服务方法
  }
}

进阶:可选依赖

import { Injectable, Optional, Inject } from '@nestjs/common';

@Injectable()
export class HttpService<T> {
  // 可选依赖:如果没有提供则为 undefined
  constructor(@Optional() @Inject('HTTP_OPTIONS') private httpClient: T) {}
}

4.3 Modules(模块)

模块是 NestJS 组织代码的基本单位,每个模块封装一组相关功能。

模块结构图:

┌──────────────────────────────────────────────────────────────┐
│                        AppModule (根模块)                     │
│                                                              │
│  imports: [UsersModule, CatsModule, DatabaseModule]          │
│                                                              │
│  ┌─────────────────┐  ┌─────────────────┐                   │
│  │   UsersModule   │  │   CatsModule    │                   │
│  │                 │  │                 │                   │
│  │ controllers:    │  │ controllers:    │                   │
│  │  [UsersCtrl]    │  │  [CatsCtrl]     │                   │
│  │                 │  │                 │                   │
│  │ providers:      │  │ providers:      │                   │
│  │  [UsersService] │  │  [CatsService]  │                   │
│  │                 │  │                 │                   │
│  │ exports:        │  │ exports:        │                   │
│  │  [UsersService] │  │  [CatsService]  │                   │
│  └─────────────────┘  └─────────────────┘                   │
└──────────────────────────────────────────────────────────────┘

@Module() 装饰器四大属性

@Module({
  imports: [],      // 导入其他模块,使用其导出的提供者
  controllers: [],  // 注册控制器
  providers: [],    // 注册提供者(在本模块内可用)
  exports: [],      // 导出提供者(让其他模块可以使用)
})

示例:功能模块

// cats/cats.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService],  // 导出后其他模块可以使用 CatsService
})
export class CatsModule {}
// app.module.ts(根模块)
import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],  // 导入 CatsModule
})
export class AppModule {}

全局模块

import { Module, Global } from '@nestjs/common';

@Global()  // 全局模块,不需要在其他地方 import
@Module({
  providers: [DatabaseService, ConfigService],
  exports: [DatabaseService, ConfigService],
})
export class CoreModule {}

动态模块(高级)

动态模块允许在运行时根据配置创建不同的模块实例,最典型的例子是数据库模块:

@Module({})
export class DatabaseModule {
  // forRoot 用于全局配置(通常在 AppModule 中调用)
  static forRoot(entities = [], options?): DynamicModule {
    const providers = createDatabaseProviders(options, entities);
    return {
      module: DatabaseModule,
      providers: providers,
      exports: providers,
    };
  }

  // forFeature 用于功能模块中注册特定实体
  static forFeature(entities = []): DynamicModule {
    // ...
  }
}

// 使用方式:
@Module({
  imports: [DatabaseModule.forRoot([User, Cat])],
})
export class AppModule {}

5. 请求处理管道

以下是五大请求处理机制的详细说明,理解它们的执行顺序是关键:

请求处理完整流程:

HTTP Request
     │
     ▼
[1] Middleware      ──→ next()  ──→ ...(全局/路由级)
     │
     ▼
[2] Guards          ──→ true/false 决定是否继续
     │
     ▼
[3] Interceptors    ──→ 在 handle() 调用前执行
     │
     ▼
[4] Pipes           ──→ 转换和验证参数
     │
     ▼
[5] Route Handler   ──→ 执行控制器方法
     │
     ▼
[5] Interceptors    ──→ 在 handle() 调用后执行(响应处理)
     │
     ▼
[如果抛出异常]
Exception Filters   ──→ 捕获并处理异常
     │
     ▼
HTTP Response

5.1 Middleware(中间件)

中间件在路由处理之前执行,是最先接触到请求的层。

// 类形式(推荐,可注入依赖)
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
    next();  // 必须调用,否则请求会卡住!
  }
}
// 函数形式(简洁,适合无依赖的场景)
export function logger(req: Request, res: Response, next: NextFunction) {
  console.log(`Request...`);
  next();
}

注册中间件:

// app.module.ts
@Module({ imports: [CatsModule] })
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)           // 应用中间件
      .exclude(                          // 排除某些路由
        { path: 'cats', method: RequestMethod.GET },
        'cats/(.*)',
      )
      .forRoutes(CatsController);        // 应用到指定控制器
    
    // 多个中间件按顺序执行
    consumer
      .apply(cors(), helmet(), logger)
      .forRoutes('*');
  }
}

5.2 Pipes(管道)

管道有两个核心职责:数据转换数据验证

Pipes 工作原理:

@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
                        │
                        ▼
              输入: "123" (string)
                        │
                  [ParseIntPipe]
                  parseInt("123")
                        │
                        ▼
              输出: 123 (number) ✅
              
              如果输入 "abc":
              parseInt("abc") = NaN
                        │
                        ▼
              抛出 BadRequestException ❌
}

内置管道一览

管道功能
ValidationPipe基于 class-validator 验证 DTO
ParseIntPipe字符串转整数
ParseFloatPipe字符串转浮点数
ParseBoolPipe字符串转布尔值
ParseArrayPipe字符串转数组
ParseUUIDPipe验证 UUID 格式
ParseEnumPipe验证枚举值
ParseDatePipe字符串转日期
DefaultValuePipe提供默认值

简单示例:内置管道

@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
  return this.catsService.findOne(id);  // id 已确保是数字
}

@Get()
async findAll(
  @Query('active', new DefaultValuePipe(false), ParseBoolPipe) active: boolean,
  @Query('page', new DefaultValuePipe(1), ParseIntPipe) page: number,
) {
  return this.catsService.findAll({ active, page });
}

进阶示例:使用 ValidationPipe + class-validator

npm i class-validator class-transformer
// create-cat.dto.ts
import { IsString, IsInt, IsOptional, Min, Max } from 'class-validator';

export class CreateCatDto {
  @IsString()
  name: string;

  @IsInt()
  @Min(0)
  @Max(30)
  age: number;

  @IsString()
  @IsOptional()
  breed?: string;
}
// main.ts —— 全局启用 ValidationPipe
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe({
    whitelist: true,          // 过滤 DTO 中未定义的字段
    forbidNonWhitelisted: true, // 遇到未知字段时报错
    transform: true,          // 自动类型转换
    transformOptions: {
      enableImplicitConversion: true,
    },
  }));
  await app.listen(3000);
}

高级示例:自定义管道

import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { ZodSchema } from 'zod';

@Injectable()
export class ZodValidationPipe implements PipeTransform {
  constructor(private schema: ZodSchema) {}

  transform(value: unknown, metadata: ArgumentMetadata) {
    try {
      return this.schema.parse(value);
    } catch (error) {
      throw new BadRequestException('Validation failed');
    }
  }
}

// 使用:
import { z } from 'zod';

const createCatSchema = z.object({
  name: z.string(),
  age: z.number().min(0).max(30),
  breed: z.string().optional(),
});

@Post()
@UsePipes(new ZodValidationPipe(createCatSchema))
create(@Body() createCatDto: CreateCatDto) {
  return this.catsService.create(createCatDto);
}

5.3 Guards(守卫)

守卫决定请求是否有权访问某个路由,最常见的应用是认证和授权。

Guards 决策流程:

HTTP Request → Guard.canActivate()
                      │
               ┌──────┴──────┐
               │             │
            true           false
               │             │
               ▼             ▼
          继续处理        403 Forbidden
          (下一层)         (拒绝请求)

示例:JWT 认证守卫

import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private readonly jwtService: JwtService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);
    
    if (!token) throw new UnauthorizedException();
    
    try {
      const payload = await this.jwtService.verifyAsync(token);
      request['user'] = payload;  // 将用户信息挂载到请求上
    } catch {
      throw new UnauthorizedException();
    }
    return true;
  }

  private extractTokenFromHeader(request: any): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}

示例:基于角色的权限守卫

// roles.decorator.ts
import { Reflector } from '@nestjs/core';
export const Roles = Reflector.createDecorator<string[]>();

// roles.guard.ts
@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    // 获取路由所需的角色
    const requiredRoles = this.reflector.get(Roles, context.getHandler());
    if (!requiredRoles) return true;  // 没有角色限制,放行
    
    const { user } = context.switchToHttp().getRequest();
    return requiredRoles.some(role => user?.roles?.includes(role));
  }
}

// 在控制器中使用:
@Controller('admin')
@UseGuards(AuthGuard, RolesGuard)  // 先认证,再授权
export class AdminController {
  
  @Get('users')
  @Roles(['admin', 'superadmin'])   // 只有 admin 或 superadmin 可访问
  getUsers() {
    return this.usersService.findAll();
  }
}

守卫注册范围:

// 方法级别
@UseGuards(AuthGuard)
@Get('profile')
getProfile() {}

// 控制器级别
@Controller('cats')
@UseGuards(AuthGuard)
export class CatsController {}

// 全局级别(推荐通过模块注册以支持 DI)
@Module({
  providers: [
    { provide: APP_GUARD, useClass: AuthGuard },
  ],
})
export class AppModule {}

5.4 Interceptors(拦截器)

拦截器基于 AOP(面向切面编程)思想,可以在方法执行前后绑定额外逻辑。

拦截器执行模型(使用 RxJS Observable):

Request
  │
  ▼
intercept(context, next) {
  // 前置逻辑
  console.log('Before...');      ──→  执行
  
  return next.handle().pipe(      ──→  调用实际 Handler
    tap(() => console.log('After'))  ──→  Handler 执行后
  );
}

示例集合

// 1. 日志拦截器
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const now = Date.now();
    console.log('Before...');
    
    return next.handle().pipe(
      tap(() => console.log(`After... ${Date.now() - now}ms`)),
    );
  }
}

// 2. 响应转换拦截器(统一包装响应格式)
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, { data: T }> {
  intercept(context: ExecutionContext, next: CallHandler): Observable<{ data: T }> {
    return next.handle().pipe(
      map(data => ({ data, success: true, timestamp: new Date().toISOString() }))
    );
  }
}
// 原始响应: { id: 1, name: 'Tom' }
// 包装后: { data: { id: 1, name: 'Tom' }, success: true, timestamp: '...' }

// 3. 缓存拦截器
@Injectable()
export class CacheInterceptor implements NestInterceptor {
  private cache = new Map<string, any>();

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest();
    const cacheKey = request.url;
    
    if (this.cache.has(cacheKey)) {
      return of(this.cache.get(cacheKey));  // 命中缓存,直接返回
    }
    
    return next.handle().pipe(
      tap(data => this.cache.set(cacheKey, data))
    );
  }
}

// 4. 超时拦截器
@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      timeout(5000),
      catchError(err => {
        if (err instanceof TimeoutError) {
          return throwError(() => new RequestTimeoutException());
        }
        return throwError(() => err);
      }),
    );
  }
}

5.5 Exception Filters(异常过滤器)

NestJS 内置了全局异常处理层,当抛出 HttpException 时会自动返回格式化的 JSON 响应。

异常处理流程:

任意层抛出异常
      
      
   HttpException?
  ┌────┴────┐
 Yes        No
            
            
返回对应    返回 500
HTTP状态码  Internal Server Error

自定义 ExceptionFilter 可以捕获并处理任意类型的异常

内置 HTTP 异常

// 常用内置异常
throw new BadRequestException('无效的请求参数');        // 400
throw new UnauthorizedException('请先登录');            // 401
throw new ForbiddenException('权限不足');              // 403
throw new NotFoundException('资源不存在');              // 404
throw new ConflictException('资源已存在');              // 409
throw new InternalServerErrorException('服务器错误');  // 500

// 完整示例
@Get()
async findAll() {
  const cats = await this.catsService.findAll();
  if (!cats || cats.length === 0) {
    throw new NotFoundException('No cats found');
  }
  return cats;
}

自定义异常类

// custom-exception.ts
export class BusinessException extends HttpException {
  constructor(message: string, code: string, httpStatus: number) {
    super({ message, code, success: false }, httpStatus);
  }
}

// 使用
throw new BusinessException('用户余额不足', 'INSUFFICIENT_BALANCE', HttpStatus.BAD_REQUEST);

自定义全局异常过滤器

import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';
import { Request, Response } from 'express';

@Catch()  // 捕获所有异常
export class GlobalExceptionFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    const status = exception instanceof HttpException
      ? exception.getStatus()
      : HttpStatus.INTERNAL_SERVER_ERROR;

    const message = exception instanceof HttpException
      ? exception.message
      : 'Internal server error';

    // 统一的错误响应格式
    response.status(status).json({
      success: false,
      statusCode: status,
      message,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}

// 注册为全局过滤器
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new GlobalExceptionFilter());
  await app.listen(3000);
}

6. 依赖注入进阶

自定义提供者的四种形式

标准简写:
providers: [CatsService]

完整写法(等价于上面):
providers: [{ provide: CatsService, useClass: CatsService }]

这里的 provide 就是"令牌"(Token),可以是类、字符串或 Symbol
// 1. useValue —— 注入常量值或 Mock 对象
providers: [
  {
    provide: CatsService,
    useValue: mockCatsService,  // 测试时用 Mock 替换真实服务
  },
  {
    provide: 'API_KEY',         // 字符串 Token
    useValue: 'my-secret-key',
  },
]

// 2. useClass —— 根据条件选择不同的实现类
providers: [
  {
    provide: ConfigService,
    useClass: process.env.NODE_ENV === 'development'
      ? DevConfigService
      : ProdConfigService,
  },
]

// 3. useFactory —— 工厂函数(可注入其他依赖,支持异步)
providers: [
  {
    provide: 'DATABASE_CONNECTION',
    useFactory: async (configService: ConfigService) => {
      const connection = await createDatabaseConnection({
        host: configService.get('DB_HOST'),
        port: configService.get('DB_PORT'),
      });
      return connection;
    },
    inject: [ConfigService],  // 注入到工厂函数的依赖
  },
]

// 4. useExisting —— 为现有提供者创建别名
providers: [
  LoggerService,
  {
    provide: 'LOGGER',
    useExisting: LoggerService,  // 别名,引用同一个实例
  },
]

注入自定义令牌(Token):

// 注入字符串 Token
@Injectable()
export class CatsRepository {
  constructor(
    @Inject('DATABASE_CONNECTION') private readonly db: DatabaseConnection,
    @Inject('API_KEY') private readonly apiKey: string,
  ) {}
}

7. 数据库集成实战

TypeORM 集成(最推荐)

npm install --save @nestjs/typeorm typeorm pg  # PostgreSQL
# 或
npm install --save @nestjs/typeorm typeorm mysql2  # MySQL
TypeORM + NestJS 架构:

Controller ──→ Service ──→ Repository ──→ Database
                              │
                    @InjectRepository(User)
                    private userRepo: Repository<User>
                              │
                    userRepo.find()
                    userRepo.save()
                    userRepo.delete()

完整示例

// user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, OneToMany } from 'typeorm';

@Entity('users')
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ length: 50 })
  firstName: string;

  @Column({ length: 50 })
  lastName: string;

  @Column({ unique: true })
  email: string;

  @Column({ select: false })  // 默认查询不返回密码
  password: string;

  @Column({ default: true })
  isActive: boolean;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;
}
// 2. 配置模块(app.module.ts)
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        type: 'postgres',
        host:     configService.get('DB_HOST', 'localhost'),
        port:     configService.get<number>('DB_PORT', 5432),
        username: configService.get('DB_USER', 'postgres'),
        password: configService.get('DB_PASSWORD', ''),
        database: configService.get('DB_NAME', 'nestdb'),
        autoLoadEntities: true,   // 自动加载所有通过 forFeature 注册的实体
        synchronize: true,        // ⚠️ 仅开发环境使用,生产用 migrations
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}
// 3. 功能模块(users.module.ts)
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';

@Module({
  imports: [TypeOrmModule.forFeature([User])],  // 注册 User 仓库
  providers: [UsersService],
  controllers: [UsersController],
  exports: [UsersService],
})
export class UsersModule {}
// 4. 服务层(users.service.ts)
import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly usersRepo: Repository<User>,
  ) {}

  async create(createUserDto: CreateUserDto): Promise<User> {
    // 检查邮箱是否已存在
    const existing = await this.usersRepo.findOne({ 
      where: { email: createUserDto.email } 
    });
    if (existing) throw new ConflictException('Email already exists');
    
    const user = this.usersRepo.create(createUserDto);
    return this.usersRepo.save(user);
  }

  findAll(): Promise<User[]> {
    return this.usersRepo.find({ where: { isActive: true } });
  }

  async findOne(id: number): Promise<User> {
    const user = await this.usersRepo.findOneBy({ id });
    if (!user) throw new NotFoundException(`User #${id} not found`);
    return user;
  }

  async update(id: number, updateUserDto: UpdateUserDto): Promise<User> {
    const user = await this.findOne(id);
    Object.assign(user, updateUserDto);
    return this.usersRepo.save(user);
  }

  async remove(id: number): Promise<void> {
    const user = await this.findOne(id);
    await this.usersRepo.remove(user);
  }

  // 使用 QueryBuilder 进行复杂查询
  async findActiveUsersWithPagination(page: number, limit: number) {
    const [data, total] = await this.usersRepo
      .createQueryBuilder('user')
      .where('user.isActive = :isActive', { isActive: true })
      .orderBy('user.createdAt', 'DESC')
      .skip((page - 1) * limit)
      .take(limit)
      .getManyAndCount();

    return {
      data,
      total,
      page,
      lastPage: Math.ceil(total / limit),
    };
  }
}
// 5. DTO 定义(dto/create-user.dto.ts)
import { IsEmail, IsString, MinLength, MaxLength, IsOptional } from 'class-validator';

export class CreateUserDto {
  @IsString()
  @MaxLength(50)
  firstName: string;

  @IsString()
  @MaxLength(50)
  lastName: string;

  @IsEmail()
  email: string;

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

事务处理(高级)

@Injectable()
export class OrderService {
  constructor(private dataSource: DataSource) {}

  async createOrderWithPayment(orderData: any) {
    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {
      // 操作1:创建订单
      const order = queryRunner.manager.create(Order, orderData);
      await queryRunner.manager.save(order);

      // 操作2:扣减库存
      await queryRunner.manager.decrement(
        Product, 
        { id: orderData.productId }, 
        'stock', 
        orderData.quantity
      );

      // 操作3:创建支付记录
      const payment = queryRunner.manager.create(Payment, { orderId: order.id });
      await queryRunner.manager.save(payment);

      await queryRunner.commitTransaction();  // 全部成功,提交
      return order;
    } catch (err) {
      await queryRunner.rollbackTransaction();  // 任何失败,回滚
      throw err;
    } finally {
      await queryRunner.release();  // 释放连接
    }
  }
}

8. 安全:认证与授权

JWT 认证完整实现

JWT 认证流程:

┌──────────┐                           ┌──────────────┐
│  Client  │                           │   NestJS     │
└──────────┘                           └──────────────┘
     │                                       │
     │  POST /auth/login                     │
     │  { username, password }  ──────────▶  │
     │                                       │ 验证凭证
     │                                       │ 生成 JWT
     │  ◀──────────────────────────────────  │
     │  { access_token: "eyJhbG..." }        │
     │                                       │
     │  GET /profile                         │
     │  Authorization: Bearer eyJhbG... ───▶ │
     │                                       │ 验证 JWT
     │                                       │ 解析 payload
     │  ◀──────────────────────────────────  │
     │  { userId: 1, username: "john" }      │

安装依赖

npm install --save @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt
npm install --save-dev @types/passport-jwt @types/bcrypt

完整实现

// auth/constants.ts
export const jwtConstants = {
  // ⚠️ 生产环境请使用环境变量!
  secret: process.env.JWT_SECRET || 'your-very-secret-key',
  expiresIn: '7d',
};
// users/users.service.ts(简化版)
@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User) private usersRepo: Repository<User>
  ) {}

  async findByUsername(username: string): Promise<User | null> {
    return this.usersRepo.findOne({ 
      where: { username },
      select: ['id', 'username', 'password', 'roles'],  // 包含 password 用于验证
    });
  }
}
// auth/auth.service.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';
import * as bcrypt from 'bcrypt';

@Injectable()
export class AuthService {
  constructor(
    private usersService: UsersService,
    private jwtService: JwtService,
  ) {}

  async signIn(username: string, password: string) {
    const user = await this.usersService.findByUsername(username);
    
    if (!user) throw new UnauthorizedException('Invalid credentials');
    
    // 使用 bcrypt 比对密码
    const isPasswordValid = await bcrypt.compare(password, user.password);
    if (!isPasswordValid) throw new UnauthorizedException('Invalid credentials');
    
    const payload = { sub: user.id, username: user.username, roles: user.roles };
    
    return {
      access_token: await this.jwtService.signAsync(payload),
      user: { id: user.id, username: user.username },
    };
  }

  async hashPassword(password: string): Promise<string> {
    return bcrypt.hash(password, 10);
  }
}
// auth/auth.guard.ts
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';
import { jwtConstants } from './constants';
import { IS_PUBLIC_KEY } from './decorators/public.decorator';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private jwtService: JwtService,
    private reflector: Reflector,
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    // 检查是否标记为公开路由
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (isPublic) return true;

    const request = context.switchToHttp().getRequest();
    const token = request.headers.authorization?.split(' ')[1];
    if (!token) throw new UnauthorizedException();

    try {
      const payload = await this.jwtService.verifyAsync(token, {
        secret: jwtConstants.secret,
      });
      request['user'] = payload;
    } catch {
      throw new UnauthorizedException('Token invalid or expired');
    }
    return true;
  }
}
// auth/decorators/public.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
// auth/auth.module.ts
@Module({
  imports: [
    UsersModule,
    JwtModule.registerAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        global: true,
        secret: configService.get('JWT_SECRET'),
        signOptions: { expiresIn: '7d' },
      }),
      inject: [ConfigService],
    }),
  ],
  providers: [
    AuthService,
    // 全局注册守卫
    { provide: APP_GUARD, useClass: AuthGuard },
    { provide: APP_GUARD, useClass: RolesGuard },
  ],
  controllers: [AuthController],
  exports: [AuthService],
})
export class AuthModule {}
// auth/auth.controller.ts
@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Public()           // 标记为公开,不需要 JWT
  @Post('login')
  @HttpCode(HttpStatus.OK)
  signIn(@Body() signInDto: SignInDto) {
    return this.authService.signIn(signInDto.username, signInDto.password);
  }

  @Get('profile')     // 需要 JWT(默认)
  getProfile(@Request() req) {
    return req.user;
  }
}
// 在业务控制器中使用角色
@Controller('admin')
@Roles(['admin'])   // 整个控制器需要 admin 角色
export class AdminController {
  
  @Get('stats')
  @Roles(['admin', 'analyst'])  // 方法级别覆盖
  getStats() {
    return this.statsService.get();
  }

  @Delete('users/:id')
  @Roles(['superadmin'])  // 高危操作只允许超级管理员
  deleteUser(@Param('id') id: string) {
    return this.usersService.remove(+id);
  }
}

9. 配置管理

使用 @nestjs/config

npm i --save @nestjs/config
// app.module.ts
import { ConfigModule } from '@nestjs/config';
import configuration from './config/configuration';
import * as Joi from 'joi';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,            // 全局可用,不需要在其他模块 import
      envFilePath: [
        `.env.${process.env.NODE_ENV}`,  // 先尝试加载环境特定文件
        '.env',                          // 回退到默认文件
      ],
      load: [configuration],     // 加载自定义配置文件
      cache: true,               // 缓存 process.env 访问提升性能
      // 使用 Joi 验证必要的环境变量
      validationSchema: Joi.object({
        NODE_ENV: Joi.string()
          .valid('development', 'production', 'test')
          .default('development'),
        PORT: Joi.number().default(3000),
        DATABASE_URL: Joi.string().required(),
        JWT_SECRET: Joi.string().min(32).required(),
      }),
    }),
  ],
})
export class AppModule {}
// config/configuration.ts
export default () => ({
  port: parseInt(process.env.PORT, 10) || 3000,
  database: {
    host:     process.env.DATABASE_HOST || 'localhost',
    port:     parseInt(process.env.DATABASE_PORT, 10) || 5432,
    name:     process.env.DATABASE_NAME || 'mydb',
    username: process.env.DATABASE_USER || 'postgres',
    password: process.env.DATABASE_PASSWORD || '',
  },
  jwt: {
    secret:    process.env.JWT_SECRET,
    expiresIn: process.env.JWT_EXPIRES_IN || '7d',
  },
  redis: {
    host: process.env.REDIS_HOST || 'localhost',
    port: parseInt(process.env.REDIS_PORT, 10) || 6379,
  },
});
// 在服务中使用 ConfigService
@Injectable()
export class AppService {
  constructor(private configService: ConfigService) {}

  getConfig() {
    // 访问简单环境变量
    const port = this.configService.get<number>('port');
    
    // 访问嵌套配置
    const dbHost = this.configService.get<string>('database.host');
    
    // 带默认值
    const timeout = this.configService.get<number>('timeout', 5000);
    
    return { port, dbHost, timeout };
  }
}
// 命名空间配置(更推荐的组织方式)
// config/database.config.ts
import { registerAs } from '@nestjs/config';

export default registerAs('database', () => ({
  host:     process.env.DATABASE_HOST || 'localhost',
  port:     parseInt(process.env.DATABASE_PORT, 10) || 5432,
  name:     process.env.DATABASE_NAME || 'mydb',
}));

// 使用命名空间注入(完全类型安全)
import { ConfigType } from '@nestjs/config';
import databaseConfig from './config/database.config';

@Injectable()
export class DatabaseService {
  constructor(
    @Inject(databaseConfig.KEY)
    private dbConfig: ConfigType<typeof databaseConfig>,
  ) {
    console.log(dbConfig.host);  // 完全类型提示!
  }
}

10. 微服务架构

NestJS 内置了强大的微服务支持,同样的 Guards、Pipes、Interceptors 都可以用于微服务。

微服务通信架构:

┌────────────────────┐      TCP/Redis/NATS       ┌────────────────────┐
│   API Gateway       │ ◀──────────────────────▶ │  Users Service      │
│   (HTTP Server)     │                           │  (Microservice)     │
└──────────┬─────────┘                           └────────────────────┘
           │
           │ TCP/Redis/NATS
           ▼
┌────────────────────┐      TCP/Redis/NATS       ┌────────────────────┐
│  Orders Service     │ ◀──────────────────────▶ │  Products Service   │
│  (Microservice)     │                           │  (Microservice)     │
└────────────────────┘                           └────────────────────┘

创建微服务

// main.ts(微服务启动文件)
import { NestFactory } from '@nestjs/core';
import { Transport, MicroserviceOptions } from '@nestjs/microservices';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.createMicroservice<MicroserviceOptions>(
    AppModule,
    {
      transport: Transport.TCP,
      options: { host: '0.0.0.0', port: 3001 },
    },
  );
  await app.listen();
  console.log('Microservice is listening on port 3001');
}
bootstrap();
// 微服务控制器:使用 @MessagePattern 处理请求-响应
@Controller()
export class UsersController {
  constructor(private usersService: UsersService) {}

  // 请求-响应模式
  @MessagePattern({ cmd: 'get_user' })
  getUser(@Payload() data: { id: number }) {
    return this.usersService.findOne(data.id);
  }

  @MessagePattern({ cmd: 'create_user' })
  async createUser(@Payload() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

  // 事件驱动模式(单向,不等待响应)
  @EventPattern('user_registered')
  async handleUserRegistered(@Payload() data: { userId: number; email: string }) {
    await this.emailService.sendWelcomeEmail(data.email);
    console.log(`Welcome email sent to ${data.email}`);
  }
}
// API Gateway:作为微服务的客户端
@Module({
  imports: [
    ClientsModule.registerAsync([
      {
        name: 'USERS_SERVICE',
        imports: [ConfigModule],
        useFactory: (configService: ConfigService) => ({
          transport: Transport.TCP,
          options: {
            host: configService.get('USERS_SERVICE_HOST', 'localhost'),
            port: configService.get<number>('USERS_SERVICE_PORT', 3001),
          },
        }),
        inject: [ConfigService],
      },
    ]),
  ],
})
export class UsersGatewayModule {}

@Controller('users')
export class UsersGatewayController {
  constructor(
    @Inject('USERS_SERVICE') private usersClient: ClientProxy,
  ) {}

  @Get(':id')
  getUser(@Param('id') id: string) {
    // send() 用于请求-响应,返回 Observable
    return this.usersClient.send<User>({ cmd: 'get_user' }, { id: +id });
  }

  @Post()
  createUser(@Body() createUserDto: CreateUserDto) {
    return this.usersClient.send<User>({ cmd: 'create_user' }, createUserDto);
  }

  @Post('notify')
  notifyUser(@Body() data: any) {
    // emit() 用于事件发布,不等待响应
    this.usersClient.emit('user_registered', data);
    return { message: 'Event emitted' };
  }
}

混合应用(HTTP + 微服务)

// 同一个应用同时处理 HTTP 请求和微服务消息
async function bootstrap() {
  // 创建 HTTP 应用
  const app = await NestFactory.create(AppModule);
  
  // 附加微服务
  app.connectMicroservice<MicroserviceOptions>({
    transport: Transport.TCP,
    options: { port: 3001 },
  });
  
  await app.startAllMicroservices();
  await app.listen(3000);
  console.log('HTTP: 3000, Microservice: 3001');
}

11. 测试策略

NestJS 提供了与 Jest 深度集成的测试工具,支持单元测试、集成测试和 E2E 测试。

测试金字塔:

         ┌─────────────┐
         │   E2E 测试   │  少量,测整体流程
         │  (慢,真实)  │
        ┌┴─────────────┴┐
        │  集成测试      │  适量,测模块间协作
        │ (中,部分 Mock)│
       ┌┴───────────────┴┐
       │    单元测试       │  大量,测单个类/函数
       │  (快,完全 Mock)  │
       └─────────────────┘

单元测试

// cats.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { CatsService } from './cats.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Cat } from './cat.entity';
import { Repository } from 'typeorm';

describe('CatsService', () => {
  let service: CatsService;
  let repo: Repository<Cat>;

  // Mock Repository
  const mockCatRepository = {
    find: jest.fn(),
    findOne: jest.fn(),
    save: jest.fn(),
    create: jest.fn(),
    delete: jest.fn(),
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        CatsService,
        {
          provide: getRepositoryToken(Cat),
          useValue: mockCatRepository,  // 替换真实仓库为 Mock
        },
      ],
    }).compile();

    service = module.get<CatsService>(CatsService);
    repo = module.get<Repository<Cat>>(getRepositoryToken(Cat));
  });

  afterEach(() => jest.clearAllMocks());

  describe('findAll', () => {
    it('should return an array of cats', async () => {
      const mockCats = [
        { id: 1, name: 'Tom', age: 3 },
        { id: 2, name: 'Jerry', age: 2 },
      ];
      mockCatRepository.find.mockResolvedValue(mockCats);

      const result = await service.findAll();
      
      expect(result).toEqual(mockCats);
      expect(mockCatRepository.find).toHaveBeenCalledTimes(1);
    });
  });

  describe('findOne', () => {
    it('should return a cat when found', async () => {
      const mockCat = { id: 1, name: 'Tom', age: 3 };
      mockCatRepository.findOne.mockResolvedValue(mockCat);

      const result = await service.findOne(1);
      expect(result).toEqual(mockCat);
    });

    it('should throw NotFoundException when cat not found', async () => {
      mockCatRepository.findOne.mockResolvedValue(null);

      await expect(service.findOne(999)).rejects.toThrow(NotFoundException);
    });
  });
});

E2E 测试

// test/cats.e2e-spec.ts
import * as request from 'supertest';
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication, ValidationPipe } from '@nestjs/common';
import { AppModule } from '../src/app.module';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Cat } from '../src/cats/cat.entity';

describe('CatsController (e2e)', () => {
  let app: INestApplication;

  const mockCatsRepository = {
    find: jest.fn().mockResolvedValue([
      { id: 1, name: 'Tom', age: 3, breed: 'Persian' },
    ]),
    findOne: jest.fn().mockResolvedValue({ id: 1, name: 'Tom', age: 3 }),
    save: jest.fn().mockImplementation(dto => Promise.resolve({ id: 1, ...dto })),
    create: jest.fn().mockImplementation(dto => dto),
    delete: jest.fn().mockResolvedValue({ affected: 1 }),
  };

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    })
      .overrideProvider(getRepositoryToken(Cat))
      .useValue(mockCatsRepository)
      .compile();

    app = moduleFixture.createNestApplication();
    app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
    await app.init();
  });

  afterAll(async () => await app.close());

  it('GET /cats → 200', () => {
    return request(app.getHttpServer())
      .get('/cats')
      .expect(200)
      .expect(res => {
        expect(Array.isArray(res.body)).toBe(true);
      });
  });

  it('POST /cats → 201', () => {
    return request(app.getHttpServer())
      .post('/cats')
      .send({ name: 'Tom', age: 3, breed: 'Persian' })
      .expect(201)
      .expect(res => {
        expect(res.body).toHaveProperty('id');
        expect(res.body.name).toBe('Tom');
      });
  });

  it('POST /cats → 400 when invalid data', () => {
    return request(app.getHttpServer())
      .post('/cats')
      .send({ name: 'Tom', age: 'not-a-number' })  // age 应为数字
      .expect(400);
  });
});

12. 进阶技巧与最佳实践

12.1 自定义装饰器

// 提取当前登录用户的装饰器
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const CurrentUser = createParamDecorator(
  (data: string | undefined, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const user = request.user;
    return data ? user?.[data] : user;
  },
);

// 使用方式
@Get('profile')
getProfile(@CurrentUser() user: User) {
  return user;
}

@Get('profile/id')
getProfileId(@CurrentUser('id') userId: number) {
  return userId;
}
// 组合多个装饰器为一个
import { applyDecorators, UseGuards, SetMetadata } from '@nestjs/common';
import { ApiBearerAuth } from '@nestjs/swagger';

export function Auth(...roles: string[]) {
  return applyDecorators(
    SetMetadata('roles', roles),
    UseGuards(AuthGuard, RolesGuard),
    ApiBearerAuth(),
  );
}

// 使用方式(非常简洁!)
@Get('admin')
@Auth('admin')
getAdminData() { ... }

12.2 生命周期钩子

@Injectable()
export class DatabaseService implements OnModuleInit, OnModuleDestroy {
  private connection: Connection;

  async onModuleInit() {
    // 模块初始化时执行(适合建立连接)
    this.connection = await createConnection();
    console.log('Database connected');
  }

  async onModuleDestroy() {
    // 模块销毁时执行(适合释放资源)
    await this.connection.close();
    console.log('Database connection closed');
  }
}

// 应用级别的钩子
@Injectable()
export class AppService implements OnApplicationBootstrap, BeforeApplicationShutdown {
  async onApplicationBootstrap() {
    // 所有模块初始化完成后执行
    console.log('Application is ready');
  }

  async beforeApplicationShutdown(signal?: string) {
    // 应用关闭前执行(优雅关闭)
    console.log(`Received ${signal}. Gracefully shutting down...`);
  }
}

12.3 注入作用域(Scopes)

import { Injectable, Scope } from '@nestjs/common';

// 单例(默认):整个应用共享同一实例
@Injectable()
export class SingletonService {}

// 请求级:每个 HTTP 请求创建新实例
@Injectable({ scope: Scope.REQUEST })
export class RequestScopedService {
  constructor(@Inject(REQUEST) private request: Request) {}
  
  getUserId() {
    return this.request['user']?.id;
  }
}

// 瞬态:每次注入都创建新实例
@Injectable({ scope: Scope.TRANSIENT })
export class TransientService {}

12.4 OpenAPI(Swagger)文档

npm install --save @nestjs/swagger
// main.ts
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  const config = new DocumentBuilder()
    .setTitle('My API')
    .setDescription('The API documentation')
    .setVersion('1.0')
    .addBearerAuth()  // 添加 JWT 认证
    .build();
    
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);  // 访问 /api 查看文档

  await app.listen(3000);
}
// 在 DTO 中添加 Swagger 注解
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

export class CreateCatDto {
  @ApiProperty({ description: '猫的名字', example: 'Tom' })
  @IsString()
  name: string;

  @ApiProperty({ description: '年龄', example: 3, minimum: 0, maximum: 30 })
  @IsInt()
  @Min(0)
  age: number;

  @ApiPropertyOptional({ description: '品种', example: 'Persian' })
  @IsString()
  @IsOptional()
  breed?: string;
}

// 在控制器中添加 Swagger 注解
@ApiTags('cats')
@Controller('cats')
export class CatsController {
  @ApiOperation({ summary: '获取所有猫' })
  @ApiResponse({ status: 200, description: '成功', type: [Cat] })
  @Get()
  findAll() { ... }

  @ApiOperation({ summary: '创建猫' })
  @ApiResponse({ status: 201, description: '创建成功', type: Cat })
  @ApiResponse({ status: 400, description: '参数错误' })
  @Post()
  create(@Body() createCatDto: CreateCatDto) { ... }
}

12.5 性能优化技巧

// 1. 使用 Fastify 替代 Express(性能约提升 2-3x)
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';

const app = await NestFactory.create<NestFastifyApplication>(
  AppModule,
  new FastifyAdapter({ logger: true }),
);
await app.listen(3000, '0.0.0.0');

// 2. 启用 SWC 编译器(比 tsc 快 20 倍!)
// nest start -b swc
// nest build -b swc

// 3. 启用响应压缩
import * as compression from 'compression';
app.use(compression());

// 4. 使用 Helmet 设置安全 HTTP 头
import helmet from 'helmet';
app.use(helmet());

// 5. 启用 CORS
app.enableCors({
  origin: ['https://myapp.com', 'https://admin.myapp.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  credentials: true,
});

// 6. 全局前缀
app.setGlobalPrefix('api/v1');
// 所有路由变为 /api/v1/cats、/api/v1/users 等

13. 完整项目实战示例

博客系统 API(综合示例)

下面是一个整合了上述所有概念的真实项目结构:

src/
├── main.ts
├── app.module.ts
├── config/
│   ├── configuration.ts
│   └── database.config.ts
├── common/
│   ├── decorators/
│   │   ├── current-user.decorator.ts
│   │   ├── public.decorator.ts
│   │   └── roles.decorator.ts
│   ├── filters/
│   │   └── global-exception.filter.ts
│   ├── guards/
│   │   ├── auth.guard.ts
│   │   └── roles.guard.ts
│   ├── interceptors/
│   │   ├── logging.interceptor.ts
│   │   └── transform.interceptor.ts
│   └── pipes/
│       └── validation.pipe.ts
├── auth/
│   ├── auth.module.ts
│   ├── auth.controller.ts
│   └── auth.service.ts
├── users/
│   ├── users.module.ts
│   ├── users.controller.ts
│   ├── users.service.ts
│   ├── user.entity.ts
│   └── dto/
│       ├── create-user.dto.ts
│       └── update-user.dto.ts
└── posts/
    ├── posts.module.ts
    ├── posts.controller.ts
    ├── posts.service.ts
    ├── post.entity.ts
    └── dto/
        ├── create-post.dto.ts
        └── query-posts.dto.ts

posts/post.entity.ts

import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, CreateDateColumn } from 'typeorm';
import { User } from '../users/user.entity';

@Entity('posts')
export class Post {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column('text')
  content: string;

  @Column({ default: false })
  isPublished: boolean;

  @ManyToOne(() => User, user => user.posts, { eager: true })
  author: User;

  @CreateDateColumn()
  createdAt: Date;
}

posts/dto/query-posts.dto.ts

import { IsOptional, IsBoolean, IsInt, Min, Max } from 'class-validator';
import { Transform, Type } from 'class-transformer';

export class QueryPostsDto {
  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  page?: number = 1;

  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  @Max(100)
  limit?: number = 10;

  @IsOptional()
  @Transform(({ value }) => value === 'true')
  @IsBoolean()
  published?: boolean;
}

posts/posts.service.ts

@Injectable()
export class PostsService {
  constructor(
    @InjectRepository(Post)
    private postsRepo: Repository<Post>,
  ) {}

  async create(createPostDto: CreatePostDto, author: User): Promise<Post> {
    const post = this.postsRepo.create({ ...createPostDto, author });
    return this.postsRepo.save(post);
  }

  async findAll(queryDto: QueryPostsDto) {
    const { page, limit, published } = queryDto;

    const queryBuilder = this.postsRepo
      .createQueryBuilder('post')
      .leftJoinAndSelect('post.author', 'author')
      .orderBy('post.createdAt', 'DESC');

    if (published !== undefined) {
      queryBuilder.andWhere('post.isPublished = :published', { published });
    }

    const [data, total] = await queryBuilder
      .skip((page - 1) * limit)
      .take(limit)
      .getManyAndCount();

    return {
      data,
      meta: {
        total,
        page,
        limit,
        lastPage: Math.ceil(total / limit),
      },
    };
  }

  async findOne(id: number): Promise<Post> {
    const post = await this.postsRepo.findOne({
      where: { id },
      relations: ['author'],
    });
    if (!post) throw new NotFoundException(`Post #${id} not found`);
    return post;
  }

  async update(id: number, updateDto: UpdatePostDto, userId: number): Promise<Post> {
    const post = await this.findOne(id);
    if (post.author.id !== userId) {
      throw new ForbiddenException('You can only edit your own posts');
    }
    Object.assign(post, updateDto);
    return this.postsRepo.save(post);
  }

  async remove(id: number, userId: number): Promise<void> {
    const post = await this.findOne(id);
    if (post.author.id !== userId) {
      throw new ForbiddenException('You can only delete your own posts');
    }
    await this.postsRepo.remove(post);
  }

  async publish(id: number): Promise<Post> {
    const post = await this.findOne(id);
    post.isPublished = true;
    return this.postsRepo.save(post);
  }
}

posts/posts.controller.ts

import {
  Controller, Get, Post, Put, Delete, Patch,
  Body, Param, Query, ParseIntPipe,
  HttpCode, HttpStatus,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
import { PostsService } from './posts.service';
import { CreatePostDto } from './dto/create-post.dto';
import { UpdatePostDto } from './dto/update-post.dto';
import { QueryPostsDto } from './dto/query-posts.dto';
import { CurrentUser } from '../common/decorators/current-user.decorator';
import { Public } from '../common/decorators/public.decorator';
import { Roles } from '../common/decorators/roles.decorator';
import { User } from '../users/user.entity';

@ApiTags('posts')
@ApiBearerAuth()
@Controller('posts')
export class PostsController {
  constructor(private readonly postsService: PostsService) {}

  // ✅ 公开接口:任何人都可以查看已发布文章
  @Public()
  @Get()
  @ApiOperation({ summary: '获取文章列表' })
  findAll(@Query() queryDto: QueryPostsDto) {
    return this.postsService.findAll(queryDto);
  }

  // ✅ 公开接口:查看单篇文章
  @Public()
  @Get(':id')
  @ApiOperation({ summary: '获取单篇文章' })
  findOne(@Param('id', ParseIntPipe) id: number) {
    return this.postsService.findOne(id);
  }

  // 🔒 需要登录:创建文章
  @Post()
  @ApiOperation({ summary: '创建文章' })
  create(@Body() createPostDto: CreatePostDto, @CurrentUser() user: User) {
    return this.postsService.create(createPostDto, user);
  }

  // 🔒 需要登录:更新自己的文章
  @Put(':id')
  @ApiOperation({ summary: '更新文章' })
  update(
    @Param('id', ParseIntPipe) id: number,
    @Body() updatePostDto: UpdatePostDto,
    @CurrentUser('id') userId: number,
  ) {
    return this.postsService.update(id, updatePostDto, userId);
  }

  // 🔒 需要登录:删除自己的文章
  @Delete(':id')
  @HttpCode(HttpStatus.NO_CONTENT)
  @ApiOperation({ summary: '删除文章' })
  remove(
    @Param('id', ParseIntPipe) id: number,
    @CurrentUser('id') userId: number,
  ) {
    return this.postsService.remove(id, userId);
  }

  // 🔐 仅管理员:发布文章
  @Patch(':id/publish')
  @Roles(['admin'])
  @ApiOperation({ summary: '发布文章(仅管理员)' })
  publish(@Param('id', ParseIntPipe) id: number) {
    return this.postsService.publish(id);
  }
}

main.ts(完整启动配置)

import { NestFactory, Reflector } from '@nestjs/core';
import { ValidationPipe, ClassSerializerInterceptor } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import * as compression from 'compression';
import helmet from 'helmet';
import { AppModule } from './app.module';
import { GlobalExceptionFilter } from './common/filters/global-exception.filter';
import { TransformInterceptor } from './common/interceptors/transform.interceptor';
import { LoggingInterceptor } from './common/interceptors/logging.interceptor';

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    logger: ['error', 'warn', 'log'],
  });

  // ── 安全中间件 ──────────────────────────────────────────────────
  app.use(helmet());
  app.use(compression());
  app.enableCors({
    origin: process.env.ALLOWED_ORIGINS?.split(',') || '*',
    credentials: true,
  });

  // ── 全局前缀 ────────────────────────────────────────────────────
  app.setGlobalPrefix('api/v1');

  // ── 全局管道(数据验证)─────────────────────────────────────────
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      forbidNonWhitelisted: true,
      transform: true,
      transformOptions: { enableImplicitConversion: true },
    }),
  );

  // ── 全局拦截器 ──────────────────────────────────────────────────
  const reflector = app.get(Reflector);
  app.useGlobalInterceptors(
    new LoggingInterceptor(),
    new TransformInterceptor(),
    new ClassSerializerInterceptor(reflector),  // 自动应用 @Exclude() 等序列化装饰器
  );

  // ── 全局异常过滤器 ──────────────────────────────────────────────
  app.useGlobalFilters(new GlobalExceptionFilter());

  // ── Swagger 文档 ────────────────────────────────────────────────
  if (process.env.NODE_ENV !== 'production') {
    const config = new DocumentBuilder()
      .setTitle('Blog API')
      .setDescription('博客系统接口文档')
      .setVersion('1.0')
      .addBearerAuth(
        { type: 'http', scheme: 'bearer', bearerFormat: 'JWT' },
        'access-token',
      )
      .build();
    const document = SwaggerModule.createDocument(app, config);
    SwaggerModule.setup('api/docs', app, document, {
      swaggerOptions: { persistAuthorization: true },
    });
    console.log('📚 Swagger docs: http://localhost:3000/api/docs');
  }

  // ── 优雅关闭 ────────────────────────────────────────────────────
  app.enableShutdownHooks();

  const port = process.env.PORT ?? 3000;
  await app.listen(port);
  console.log(`🚀 Application running on: http://localhost:${port}/api/v1`);
}
bootstrap();

common/interceptors/transform.interceptor.ts(统一响应格式)

import {
  Injectable, NestInterceptor, ExecutionContext,
  CallHandler, HttpStatus,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

export interface StandardResponse<T> {
  success: boolean;
  statusCode: number;
  data: T;
  timestamp: string;
}

@Injectable()
export class TransformInterceptor<T>
  implements NestInterceptor<T, StandardResponse<T>>
{
  intercept(
    context: ExecutionContext,
    next: CallHandler,
  ): Observable<StandardResponse<T>> {
    const response = context.switchToHttp().getResponse();

    return next.handle().pipe(
      map(data => ({
        success: true,
        statusCode: response.statusCode ?? HttpStatus.OK,
        data,
        timestamp: new Date().toISOString(),
      })),
    );
  }
}

附录:核心概念速查卡

装饰器速查表

HTTP 方法装饰器:
  @Get('/path')   @Post()   @Put()   @Patch()   @Delete()   @Options()   @Head()

参数装饰器:
  @Param('id')      路径参数      /users/:id
  @Query('page')    查询参数      /users?page=1
  @Body()           请求体        POST /users { name: 'Tom' }
  @Headers('auth')  请求头
  @Req()            完整 Request 对象
  @Res()            完整 Response 对象(会绕过 NestJS 响应机制)

响应控制:
  @HttpCode(201)           修改默认状态码
  @Header('X-Key', 'val')  设置响应头
  @Redirect('/other', 301) 重定向

作用域装饰器:
  @UseGuards(Guard)              应用守卫
  @UseInterceptors(Interceptor)  应用拦截器
  @UsePipes(Pipe)                应用管道
  @UseFilters(Filter)            应用异常过滤器

元数据装饰器:
  @SetMetadata('key', value)     设置自定义元数据
  Reflector.createDecorator()    创建类型安全的元数据装饰器

全局注册方式对比

┌──────────────────┬────────────────────────────┬────────────────────────────┐
 组件              main.ts 方式                模块方式(推荐,支持DI)    
├──────────────────┼────────────────────────────┼────────────────────────────┤
 Guard             app.useGlobalGuards()        { provide: APP_GUARD,      
                                                 useClass: AuthGuard }    
├──────────────────┼────────────────────────────┼────────────────────────────┤
 Pipe              app.useGlobalPipes()         { provide: APP_PIPE,       
                                                 useClass: ValidationPipe}
├──────────────────┼────────────────────────────┼────────────────────────────┤
 Interceptor       app.useGlobalInterceptors()  { provide: APP_INTERCEPTOR,
                                                 useClass: LoggingInter.} 
├──────────────────┼────────────────────────────┼────────────────────────────┤
 Filter            app.useGlobalFilters()       { provide: APP_FILTER,     
                                                 useClass: ExceptionFilter}
└──────────────────┴────────────────────────────┴────────────────────────────┘

模块通信规则

模块间通信的唯一合法方式:

┌─────────────────────────────────────────┐
│ Module A                                │
│   providers: [ServiceA]                 │
│   exports: [ServiceA]  ◀── 必须显式导出 │
└───────────────────┬─────────────────────┘
                    │
                    │ 只有通过 imports 才能使用
                    ▼
┌─────────────────────────────────────────┐
│ Module B                                │
│   imports: [ModuleA]  ◀── 必须导入模块  │
│   providers: [ServiceB]                 │
│                                         │
│   ServiceB 现在可以注入 ServiceA        │
└─────────────────────────────────────────┘

总结

学完本文,你掌握了 NestJS 的完整知识体系:

基础层:安装 → 项目结构 → Controllers → Providers → Modules

请求处理层:Middleware → Guards → Interceptors → Pipes → Exception Filters

进阶特性:自定义提供者(useValue/useClass/useFactory/useExisting)→ 动态模块 → 注入作用域 → 生命周期钩子

数据层:TypeORM 集成 → Repository 模式 → 关系映射 → 事务处理

安全层:JWT 认证 → 角色授权 → 全局守卫 + 公开路由例外

配置层:@nestjs/config → 环境变量 → Joi 验证 → 命名空间配置

微服务层:TCP/Redis/NATS 传输 → MessagePattern → EventPattern → ClientProxy

测试层:单元测试 → E2E 测试 → Mock Repository → Override Provider

NestJS 的设计哲学是:通过约束来解放创造力。它用清晰的架构模式帮你规避了大量 Node.js 项目中常见的混乱问题,让团队可以专注于业务逻辑而非架构决策。无论是小型 API 还是复杂的企业级微服务系统,NestJS 都是构建 Node.js 后端的绝佳选择。