「周更第4期」实用JS库推荐:NestJS

20 阅读13分钟

引言

本期周更为大家推荐一个强大的Node.js后端框架——NestJS。NestJS是一个用于构建高效、可扩展的Node.js服务器端应用程序的框架,它使用TypeScript构建,并结合了OOP(面向对象编程)、FP(函数式编程)和FRP(函数响应式编程)的元素。

库介绍

基本信息

  • 库名称:NestJS
  • GitHub Stars:67,000+
  • 维护状态:活跃维护
  • 兼容性:Node.js 16+
  • 包大小:核心包约 2MB
  • 依赖关系:基于Express/Fastify,支持TypeScript

核心特性

  1. TypeScript优先:完全使用TypeScript编写,提供强类型支持
  2. 装饰器模式:大量使用装饰器,代码简洁优雅
  3. 依赖注入:内置强大的依赖注入系统
  4. 模块化架构:清晰的模块化结构,易于维护
  5. 多种传输层:支持HTTP、WebSocket、微服务等
  6. 丰富的生态:集成了大量第三方库和工具

安装使用

全局安装CLI工具

pnpm install -g @nestjs/cli

创建新项目

nest new my-nestjs-app
cd my-nestjs-app
pnpm start:dev

基础项目结构

src/
├── app.controller.ts      # 应用控制器
├── app.module.ts          # 应用根模块
├── app.service.ts         # 应用服务
└── main.ts               # 应用入口文件

应用入口文件

// main.ts
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';

const bootstrap = async (): Promise<void> => {
  const app = await NestFactory.create(AppModule);
  
  // 启用全局验证管道
  app.useGlobalPipes(new ValidationPipe({
    whitelist: true,
    forbidNonWhitelisted: true,
    transform: true,
  }));
  
  // 配置Swagger文档
  const config = new DocumentBuilder()
    .setTitle('NestJS API')
    .setDescription('API文档')
    .setVersion('1.0')
    .build();
  
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api/docs', app, document);
  
  await app.listen(3000);
};

bootstrap();

核心概念详解

1. 控制器 (Controllers)

控制器负责处理传入的请求并向客户端返回响应。使用装饰器定义路由和HTTP方法:

import { Controller, Get, Post, Body, Param, ParseIntPipe } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';

@ApiTags('用户管理')
@Controller('api/users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  @ApiOperation({ summary: '获取所有用户' })
  @ApiResponse({ status: 200, description: '获取成功' })
  async findAll() {
    return await this.usersService.findAll();
  }

  @Get(':id')
  @ApiOperation({ summary: '根据ID获取用户' })
  async findOne(@Param('id', ParseIntPipe) id: number) {
    return await this.usersService.findOne(id);
  }

  @Post()
  @ApiOperation({ summary: '创建新用户' })
  @ApiResponse({ status: 201, description: '创建成功' })
  async create(@Body() createUserDto: CreateUserDto) {
    return await this.usersService.create(createUserDto);
  }
}

2. 服务 (Services)

服务用于处理业务逻辑,通过依赖注入在控制器中使用。使用@Injectable()装饰器标记:

import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';
import { CreateUserDto } from './dto/create-user.dto';

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

  async create(createUserDto: CreateUserDto): Promise<User> {
    const user = this.usersRepository.create(createUserDto);
    return await this.usersRepository.save(user);
  }

  async findAll(): Promise<User[]> {
    return await this.usersRepository.find({
      order: { createdAt: 'DESC' },
    });
  }

  async findOne(id: number): Promise<User> {
    const user = await this.usersRepository.findOne({ where: { id } });
    if (!user) {
      throw new NotFoundException(`ID为 ${id} 的用户不存在`);
    }
    return user;
  }

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

3. 数据传输对象 (DTOs)

DTO用于定义数据结构和验证规则,确保数据的完整性和类型安全:

import { IsString, IsEmail, IsNumber, Min, Max } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';

export class CreateUserDto {
  @ApiProperty({ description: '用户名', example: 'john_doe' })
  @IsString()
  readonly username: string;

  @ApiProperty({ description: '邮箱地址', example: 'john@example.com' })
  @IsEmail()
  readonly email: string;

  @ApiProperty({ description: '用户年龄', example: 25 })
  @IsNumber()
  @Min(1)
  @Max(120)
  readonly age: number;
}

4. 实体 (Entities)

实体定义数据库表结构,使用TypeORM装饰器:

import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm';

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

  @Column({ unique: true, length: 50 })
  username: string;

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

  @Column({ type: 'int', unsigned: true })
  age: number;

  @CreateDateColumn()
  createdAt: Date;
}

5. 模块 (Modules)

模块是组织应用程序结构的基本单元,将相关组件组合在一起:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { User } from './entities/user.entity';

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService]
})
export class UsersModule {}

6. 中间件 (Middleware)

中间件是在路由处理程序之前调用的函数,用于处理请求和响应:

import { Injectable, NestMiddleware, Logger } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  private readonly logger = new Logger('HTTP');

  use(req: Request, res: Response, next: NextFunction): void {
    const { method, originalUrl, ip } = req;
    const startTime = Date.now();

    res.on('finish', () => {
      const { statusCode } = res;
      const responseTime = Date.now() - startTime;
      this.logger.log(`${method} ${originalUrl} ${statusCode} +${responseTime}ms`);
    });

    next();
  }
}

7. 守卫 (Guards)

守卫用于实现认证和授权逻辑:

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

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const token = request.headers.authorization;

    if (!token || !this.validateToken(token)) {
      throw new UnauthorizedException('无效的访问令牌');
    }

    return true;
  }

  private validateToken(token: string): boolean {
    // 实际项目中应该验证JWT token
    return token === 'Bearer valid-token';
  }
}

8. 拦截器 (Interceptors)

拦截器用于在方法执行前后添加额外逻辑:

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

@Injectable()
export class ResponseInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      map((data) => ({
        success: true,
        timestamp: new Date().toISOString(),
        data,
      })),
    );
  }
}

9. 异常过滤器 (Exception Filters)

异常过滤器用于处理应用程序中抛出的异常:

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

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
  catch(exception: unknown, host: ArgumentsHost): void {
    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.getResponse()
      : '服务器内部错误';

    response.status(status).json({
      success: false,
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
      message,
    });
  }
}

10. 管道 (Pipes)

管道用于数据转换和验证:

import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { validate } from 'class-validator';
import { plainToClass } from 'class-transformer';

@Injectable()
export class ValidationPipe implements PipeTransform<any> {
  async transform(value: any, { metatype }: ArgumentMetadata) {
    if (!metatype || !this.toValidate(metatype)) {
      return value;
    }
    
    const object = plainToClass(metatype, value);
    const errors = await validate(object);
    
    if (errors.length > 0) {
      throw new BadRequestException('数据验证失败');
    }
    
    return value;
  }

  private toValidate(metatype: Function): boolean {
    const types: Function[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  }
}

11. 自定义装饰器 (Custom Decorators)

创建自定义装饰器来简化代码:

import { createParamDecorator, ExecutionContext } from '@nestjs/common';

// 获取当前用户装饰器
export const CurrentUser = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  },
);

// 使用示例
@Get('profile')
async getProfile(@CurrentUser() user: User) {
  return user;
}

12. 配置管理 (Configuration)

使用ConfigModule管理应用配置:

import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class AppConfigService {
  constructor(private configService: ConfigService) {}

  get port(): number {
    return this.configService.get<number>('PORT', 3000);
  }

  get databaseUrl(): string {
    return this.configService.get<string>('DATABASE_URL');
  }

  get jwtSecret(): string {
    return this.configService.get<string>('JWT_SECRET');
  }
}

13. 缓存 (Caching)

内置缓存支持,提高应用性能:

import { Injectable, CACHE_MANAGER, Inject } from '@nestjs/common';
import { Cache } from 'cache-manager';

@Injectable()
export class CacheService {
  constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {}

  async get<T>(key: string): Promise<T | undefined> {
    return await this.cacheManager.get<T>(key);
  }

  async set(key: string, value: any, ttl: number = 300): Promise<void> {
    await this.cacheManager.set(key, value, ttl);
  }

  async del(key: string): Promise<void> {
    await this.cacheManager.del(key);
  }
}

// 在控制器中使用缓存装饰器
@Get(':id')
@CacheKey('user_profile')
@CacheTTL(300)
async findOne(@Param('id') id: string) {
  return this.usersService.findOne(+id);
}

14. 任务调度 (Task Scheduling)

使用@nestjs/schedule进行定时任务:

import { Injectable } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';

@Injectable()
export class TasksService {
  @Cron(CronExpression.EVERY_10_SECONDS)
  handleCron() {
    console.log('每10秒执行一次的任务');
  }

  @Cron('0 0 * * *') // 每天午夜执行
  async handleDailyTask() {
    console.log('执行每日数据清理任务');
    // 执行数据清理逻辑
  }
}

15. 单元测试 (Unit Testing)

NestJS提供了完整的测试支持:

import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { User } from './entities/user.entity';

describe('UsersService', () => {
  let service: UsersService;
  let mockRepository: any;

  beforeEach(async () => {
    mockRepository = {
      find: jest.fn(),
      findOne: jest.fn(),
      create: jest.fn(),
      save: jest.fn(),
      remove: jest.fn(),
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UsersService,
        {
          provide: getRepositoryToken(User),
          useValue: mockRepository,
        },
      ],
    }).compile();

    service = module.get<UsersService>(UsersService);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  it('should find all users', async () => {
    const users = [{ id: 1, username: 'test' }];
    mockRepository.find.mockReturnValue(users);

    const result = await service.findAll();
    expect(result).toEqual(users);
    expect(mockRepository.find).toHaveBeenCalled();
  });
});

实际应用场景

1. RESTful API开发

NestJS非常适合构建RESTful API,提供了完整的HTTP方法支持和参数验证。

2. 微服务架构

支持多种传输层(TCP、Redis、NATS等),适合构建微服务系统。

3. GraphQL API

内置GraphQL支持,可以快速构建GraphQL服务。

4. WebSocket应用

支持WebSocket和Socket.io,适合实时应用开发。

5. 企业级应用

完整的架构支持、安全特性和监控能力,适合大型企业应用。

6. 电商平台后端

支持复杂的业务逻辑、用户管理、订单处理等功能。

高级功能特性

WebSocket 实时通信

NestJS提供了完整的WebSocket支持:

import { WebSocketGateway, WebSocketServer, SubscribeMessage, MessageBody } from '@nestjs/websockets';
import { Server } from 'socket.io';

@WebSocketGateway({
  cors: {
    origin: '*',
  },
})
export class ChatGateway {
  @WebSocketServer()
  server: Server;

  @SubscribeMessage('message')
  handleMessage(@MessageBody() data: string): void {
    this.server.emit('message', data);
  }

  @SubscribeMessage('join-room')
  handleJoinRoom(@MessageBody() room: string): void {
    // 加入房间逻辑
    this.server.to(room).emit('user-joined', '新用户加入');
  }
}

GraphQL 集成

支持GraphQL API开发:

import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { UsersService } from './users.service';
import { User } from './entities/user.entity';
import { CreateUserInput } from './dto/create-user.input';

@Resolver(() => User)
export class UsersResolver {
  constructor(private readonly usersService: UsersService) {}

  @Query(() => [User], { name: 'users' })
  findAll() {
    return this.usersService.findAll();
  }

  @Query(() => User, { name: 'user' })
  findOne(@Args('id') id: number) {
    return this.usersService.findOne(id);
  }

  @Mutation(() => User)
  createUser(@Args('createUserInput') createUserInput: CreateUserInput) {
    return this.usersService.create(createUserInput);
  }
}

微服务通信

支持多种微服务通信方式:

import { Controller } from '@nestjs/common';
import { MessagePattern, ClientProxy, Client, Transport } from '@nestjs/microservices';

@Controller()
export class MicroserviceController {
  @Client({
    transport: Transport.TCP,
    options: { host: 'localhost', port: 8877 },
  })
  client: ClientProxy;

  @MessagePattern({ cmd: 'sum' })
  accumulate(data: number[]): number {
    return (data || []).reduce((a, b) => a + b, 0);
  }

  async callMicroservice() {
    const pattern = { cmd: 'sum' };
    const payload = [1, 2, 3];
    return this.client.send<number>(pattern, payload);
  }
}

安全特性

NestJS提供了多种安全功能:

import { Injectable } 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 validateUser(username: string, password: string): Promise<any> {
    const user = await this.usersService.findByUsername(username);
    if (user && await bcrypt.compare(password, user.password)) {
      const { password, ...result } = user;
      return result;
    }
    return null;
  }

  async login(user: any) {
    const payload = { username: user.username, sub: user.userId };
    return {
      access_token: this.jwtService.sign(payload),
      refresh_token: this.jwtService.sign(payload, { expiresIn: '7d' }),
    };
  }

  async hashPassword(password: string): Promise<string> {
    const saltRounds = 10;
    return await bcrypt.hash(password, saltRounds);
  }
}

健康检查

监控应用程序健康状态:

import { Controller, Get } from '@nestjs/common';
import { HealthCheckService, HttpHealthIndicator, TypeOrmHealthIndicator, HealthCheck } from '@nestjs/terminus';

@Controller('health')
export class HealthController {
  constructor(
    private health: HealthCheckService,
    private http: HttpHealthIndicator,
    private db: TypeOrmHealthIndicator,
  ) {}

  @Get()
  @HealthCheck()
  check() {
    return this.health.check([
      () => this.http.pingCheck('nestjs-docs', 'https://docs.nestjs.com'),
      () => this.db.pingCheck('database'),
    ]);
  }
}

文件上传处理

使用Multer处理文件上传:

import { Controller, Post, UseInterceptors, UploadedFile, UploadedFiles } from '@nestjs/common';
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { extname } from 'path';

@Controller('upload')
export class UploadController {
  // 单文件上传
  @Post('single')
  @UseInterceptors(
    FileInterceptor('file', {
      storage: diskStorage({
        destination: './uploads',
        filename: (req, file, callback) => {
          const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
          callback(null, file.fieldname + '-' + uniqueSuffix + extname(file.originalname));
        },
      }),
      fileFilter: (req, file, callback) => {
        if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
          return callback(new Error('只允许上传图片文件'), false);
        }
        callback(null, true);
      },
      limits: {
        fileSize: 5 * 1024 * 1024, // 5MB
      },
    }),
  )
  uploadSingle(@UploadedFile() file: Express.Multer.File) {
    return {
      message: '文件上传成功',
      filename: file.filename,
      originalname: file.originalname,
      size: file.size,
      path: file.path,
    };
  }

  // 多文件上传
  @Post('multiple')
  @UseInterceptors(
    FilesInterceptor('files', 10, {
      storage: diskStorage({
        destination: './uploads',
        filename: (req, file, callback) => {
          const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
          callback(null, file.fieldname + '-' + uniqueSuffix + extname(file.originalname));
        },
      }),
    }),
  )
  uploadMultiple(@UploadedFiles() files: Array<Express.Multer.File>) {
    return {
      message: '文件上传成功',
      files: files.map(file => ({
        filename: file.filename,
        originalname: file.originalname,
        size: file.size,
        path: file.path,
      })),
    };
  }
}

邮件发送服务

使用Nodemailer发送邮件:

import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as nodemailer from 'nodemailer';

@Injectable()
export class MailService {
  private transporter: nodemailer.Transporter;

  constructor(private configService: ConfigService) {
    this.transporter = nodemailer.createTransporter({
      host: this.configService.get('MAIL_HOST'),
      port: this.configService.get('MAIL_PORT'),
      secure: false, // true for 465, false for other ports
      auth: {
        user: this.configService.get('MAIL_USER'),
        pass: this.configService.get('MAIL_PASS'),
      },
    });
  }

  /**
   * 发送简单文本邮件
   * @param to 收件人邮箱
   * @param subject 邮件主题
   * @param text 邮件内容
   */
  async sendMail(to: string, subject: string, text: string): Promise<void> {
    const mailOptions = {
      from: this.configService.get('MAIL_FROM'),
      to,
      subject,
      text,
    };

    await this.transporter.sendMail(mailOptions);
  }

  /**
   * 发送HTML邮件
   * @param to 收件人邮箱
   * @param subject 邮件主题
   * @param html HTML内容
   */
  async sendHtmlMail(to: string, subject: string, html: string): Promise<void> {
    const mailOptions = {
      from: this.configService.get('MAIL_FROM'),
      to,
      subject,
      html,
    };

    await this.transporter.sendMail(mailOptions);
  }

  /**
   * 发送验证码邮件
   * @param to 收件人邮箱
   * @param code 验证码
   */
  async sendVerificationCode(to: string, code: string): Promise<void> {
    const html = `
      <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
        <h2 style="color: #333;">邮箱验证</h2>
        <p>您的验证码是:</p>
        <div style="background: #f5f5f5; padding: 20px; text-align: center; font-size: 24px; font-weight: bold; color: #e74c3c;">
          ${code}
        </div>
        <p style="color: #666;">验证码有效期为10分钟,请及时使用。</p>
      </div>
    `;

    await this.sendHtmlMail(to, '邮箱验证码', html);
  }
}

// 邮件控制器
@Controller('mail')
export class MailController {
  constructor(private readonly mailService: MailService) {}

  @Post('send')
  async sendMail(@Body() sendMailDto: SendMailDto) {
    await this.mailService.sendMail(
      sendMailDto.to,
      sendMailDto.subject,
      sendMailDto.text,
    );
    return { message: '邮件发送成功' };
  }

  @Post('verification')
  async sendVerification(@Body() { email }: { email: string }) {
    const code = Math.random().toString().slice(2, 8);
    await this.mailService.sendVerificationCode(email, code);
    return { message: '验证码已发送', code }; // 实际项目中不应返回验证码
  }
}

日志记录系统

使用Winston进行日志管理:

import { Injectable, LoggerService } from '@nestjs/common';
import * as winston from 'winston';
import * as DailyRotateFile from 'winston-daily-rotate-file';

@Injectable()
export class CustomLogger implements LoggerService {
  private logger: winston.Logger;

  constructor() {
    this.logger = winston.createLogger({
      level: 'info',
      format: winston.format.combine(
        winston.format.timestamp({
          format: 'YYYY-MM-DD HH:mm:ss',
        }),
        winston.format.errors({ stack: true }),
        winston.format.json(),
      ),
      defaultMeta: { service: 'nestjs-app' },
      transports: [
        // 控制台输出
        new winston.transports.Console({
          format: winston.format.combine(
            winston.format.colorize(),
            winston.format.simple(),
          ),
        }),
        // 错误日志文件
        new DailyRotateFile({
          filename: 'logs/error-%DATE%.log',
          datePattern: 'YYYY-MM-DD',
          level: 'error',
          maxSize: '20m',
          maxFiles: '14d',
        }),
        // 所有日志文件
        new DailyRotateFile({
          filename: 'logs/combined-%DATE%.log',
          datePattern: 'YYYY-MM-DD',
          maxSize: '20m',
          maxFiles: '14d',
        }),
      ],
    });
  }

  /**
   * 记录普通日志
   * @param message 日志消息
   * @param context 上下文
   */
  log(message: string, context?: string): void {
    this.logger.info(message, { context });
  }

  /**
   * 记录错误日志
   * @param message 错误消息
   * @param trace 错误堆栈
   * @param context 上下文
   */
  error(message: string, trace?: string, context?: string): void {
    this.logger.error(message, { trace, context });
  }

  /**
   * 记录警告日志
   * @param message 警告消息
   * @param context 上下文
   */
  warn(message: string, context?: string): void {
    this.logger.warn(message, { context });
  }

  /**
   * 记录调试日志
   * @param message 调试消息
   * @param context 上下文
   */
  debug(message: string, context?: string): void {
    this.logger.debug(message, { context });
  }

  /**
   * 记录详细日志
   * @param message 详细消息
   * @param context 上下文
   */
  verbose(message: string, context?: string): void {
    this.logger.verbose(message, { context });
  }
}

// 日志拦截器
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  constructor(private readonly logger: CustomLogger) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const request = context.switchToHttp().getRequest();
    const { method, url, ip } = request;
    const userAgent = request.get('User-Agent') || '';
    const startTime = Date.now();

    this.logger.log(
      `${method} ${url} - ${ip} - ${userAgent}`,
      'HTTP Request',
    );

    return next.handle().pipe(
      tap(() => {
        const response = context.switchToHttp().getResponse();
        const { statusCode } = response;
        const responseTime = Date.now() - startTime;

        this.logger.log(
          `${method} ${url} ${statusCode} - ${responseTime}ms`,
          'HTTP Response',
        );
      }),
      catchError((error) => {
        const responseTime = Date.now() - startTime;
        this.logger.error(
          `${method} ${url} - ${error.message} - ${responseTime}ms`,
          error.stack,
          'HTTP Error',
        );
        throw error;
      }),
    );
  }
}

// 在main.ts中使用自定义日志器
// app.useLogger(app.get(CustomLogger));

国际化支持

使用nestjs-i18n实现多语言支持:

import { Injectable } from '@nestjs/common';
import { I18nService } from 'nestjs-i18n';

// 语言文件结构 (src/i18n/zh/messages.json)
// {
//   "user": {
//     "welcome": "欢迎 {name}!",
//     "notFound": "用户不存在",
//     "created": "用户创建成功"
//   },
//   "validation": {
//     "required": "{field} 是必填项",
//     "email": "请输入有效的邮箱地址"
//   }
// }

// 语言文件结构 (src/i18n/en/messages.json)
// {
//   "user": {
//     "welcome": "Welcome {name}!",
//     "notFound": "User not found",
//     "created": "User created successfully"
//   },
//   "validation": {
//     "required": "{field} is required",
//     "email": "Please enter a valid email address"
//   }
// }

@Injectable()
export class I18nHelper {
  constructor(private readonly i18n: I18nService) {}

  /**
   * 获取翻译文本
   * @param key 翻译键
   * @param args 参数
   * @param lang 语言
   */
  translate(key: string, args?: any, lang?: string): string {
    return this.i18n.translate(key, { args, lang });
  }

  /**
   * 获取用户相关翻译
   * @param key 翻译键
   * @param args 参数
   * @param lang 语言
   */
  translateUser(key: string, args?: any, lang?: string): string {
    return this.translate(`user.${key}`, args, lang);
  }

  /**
   * 获取验证相关翻译
   * @param key 翻译键
   * @param args 参数
   * @param lang 语言
   */
  translateValidation(key: string, args?: any, lang?: string): string {
    return this.translate(`validation.${key}`, args, lang);
  }
}

@Controller('i18n-users')
export class I18nUsersController {
  constructor(
    private readonly usersService: UsersService,
    private readonly i18nHelper: I18nHelper,
  ) {}

  /**
   * 创建用户
   * @param createUserDto 用户数据
   * @param lang 语言
   */
  @Post()
  async create(
    @Body() createUserDto: CreateUserDto,
    @I18nLang() lang: string,
  ): Promise<{ message: string; user: any }> {
    const user = await this.usersService.create(createUserDto);
    
    return {
      message: this.i18nHelper.translateUser('created', {}, lang),
      user,
    };
  }

  /**
   * 获取用户
   * @param id 用户ID
   * @param lang 语言
   */
  @Get(':id')
  async findOne(
    @Param('id') id: string,
    @I18nLang() lang: string,
  ): Promise<{ message: string; user?: any }> {
    const user = await this.usersService.findOne(+id);
    
    if (!user) {
      throw new NotFoundException(
        this.i18nHelper.translateUser('notFound', {}, lang),
      );
    }

    return {
      message: this.i18nHelper.translateUser('welcome', { name: user.name }, lang),
      user,
    };
  }
}

// 自定义验证装饰器
export function IsEmailI18n(validationOptions?: ValidationOptions) {
  return function (object: Object, propertyName: string) {
    registerDecorator({
      name: 'isEmailI18n',
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      validator: {
        validate(value: any) {
          return typeof value === 'string' && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
        },
        defaultMessage(args: ValidationArguments) {
          return 'validation.email';
        },
      },
    });
  };
}

// 在app.module.ts中配置
// I18nModule.forRoot({
//   fallbackLanguage: 'en',
//   loaderOptions: {
//     path: path.join(__dirname, '/i18n/'),
//     watch: true,
//   },
//   resolvers: [
//     { use: QueryResolver, options: ['lang'] },
//     AcceptLanguageResolver,
//     new HeaderResolver(['x-lang']),
//   ],
// })

数据库集成

TypeORM集成

首先安装必要的依赖:

pnpm install @nestjs/typeorm typeorm mysql2

配置数据库连接和应用根模块:

// app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { UsersModule } from './users/users.module';
import { User } from './users/entities/user.entity';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        type: 'mysql',
        host: configService.get('DB_HOST', 'localhost'),
        port: configService.get('DB_PORT', 3306),
        username: configService.get('DB_USERNAME', 'root'),
        password: configService.get('DB_PASSWORD', 'password'),
        database: configService.get('DB_DATABASE', 'nestjs_demo'),
        entities: [User],
        synchronize: configService.get('NODE_ENV') !== 'production',
        logging: configService.get('NODE_ENV') === 'development',
      }),
      inject: [ConfigService],
    }),
    UsersModule,
  ],
})
export class AppModule {}

环境配置文件

创建.env文件来管理环境变量:

# .env
DB_HOST=localhost
DB_PORT=3306
DB_USERNAME=root
DB_PASSWORD=your_password
DB_DATABASE=nestjs_demo
NODE_ENV=development

数据库迁移

使用TypeORM迁移管理数据库结构变更:

// 创建迁移文件
// pnpm run migration:generate src/migrations/CreateUserTable

// src/migrations/1234567890123-CreateUserTable.ts
import { MigrationInterface, QueryRunner, Table, Index } from 'typeorm';

export class CreateUserTable1234567890123 implements MigrationInterface {
  name = 'CreateUserTable1234567890123';

  /**
   * 执行迁移 - 创建用户表
   * @param queryRunner 查询执行器
   */
  public async up(queryRunner: QueryRunner): Promise<void> {
    // 创建用户表
    await queryRunner.createTable(
      new Table({
        name: 'users',
        columns: [
          {
            name: 'id',
            type: 'int',
            isPrimary: true,
            isGenerated: true,
            generationStrategy: 'increment',
          },
          {
            name: 'email',
            type: 'varchar',
            length: '255',
            isUnique: true,
          },
          {
            name: 'name',
            type: 'varchar',
            length: '100',
          },
          {
            name: 'password',
            type: 'varchar',
            length: '255',
          },
          {
            name: 'role',
            type: 'enum',
            enum: ['admin', 'user'],
            default: "'user'",
          },
          {
            name: 'isActive',
            type: 'boolean',
            default: true,
          },
          {
            name: 'createdAt',
            type: 'timestamp',
            default: 'CURRENT_TIMESTAMP',
          },
          {
            name: 'updatedAt',
            type: 'timestamp',
            default: 'CURRENT_TIMESTAMP',
            onUpdate: 'CURRENT_TIMESTAMP',
          },
        ],
      }),
      true,
    );

    // 创建邮箱索引
    await queryRunner.createIndex(
      'users',
      new Index({
        name: 'IDX_USER_EMAIL',
        columnNames: ['email'],
      }),
    );

    // 创建角色索引
    await queryRunner.createIndex(
      'users',
      new Index({
        name: 'IDX_USER_ROLE',
        columnNames: ['role'],
      }),
    );
  }

  /**
   * 回滚迁移 - 删除用户表
   * @param queryRunner 查询执行器
   */
  public async down(queryRunner: QueryRunner): Promise<void> {
    // 删除索引
    await queryRunner.dropIndex('users', 'IDX_USER_ROLE');
    await queryRunner.dropIndex('users', 'IDX_USER_EMAIL');
    
    // 删除表
    await queryRunner.dropTable('users');
  }
}

// 添加新字段的迁移示例
// src/migrations/1234567890124-AddUserProfile.ts
export class AddUserProfile1234567890124 implements MigrationInterface {
  name = 'AddUserProfile1234567890124';

  /**
   * 添加用户资料字段
   * @param queryRunner 查询执行器
   */
  public async up(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.addColumns('users', [
      {
        name: 'avatar',
        type: 'varchar',
        length: '500',
        isNullable: true,
      },
      {
        name: 'phone',
        type: 'varchar',
        length: '20',
        isNullable: true,
      },
      {
        name: 'birthday',
        type: 'date',
        isNullable: true,
      },
    ]);

    // 添加手机号索引
    await queryRunner.createIndex(
      'users',
      new Index({
        name: 'IDX_USER_PHONE',
        columnNames: ['phone'],
      }),
    );
  }

  /**
   * 回滚用户资料字段
   * @param queryRunner 查询执行器
   */
  public async down(queryRunner: QueryRunner): Promise<void> {
    await queryRunner.dropIndex('users', 'IDX_USER_PHONE');
    await queryRunner.dropColumns('users', ['avatar', 'phone', 'birthday']);
  }
}

// TypeORM配置文件 (ormconfig.ts)
export const dataSourceOptions = {
  type: 'mysql' as const,
  host: process.env.DB_HOST || 'localhost',
  port: parseInt(process.env.DB_PORT) || 3306,
  username: process.env.DB_USERNAME || 'root',
  password: process.env.DB_PASSWORD || 'password',
  database: process.env.DB_DATABASE || 'nestjs_demo',
  entities: ['src/**/*.entity{.ts,.js}'],
  migrations: ['src/migrations/*{.ts,.js}'],
  synchronize: false, // 生产环境必须为false
  logging: process.env.NODE_ENV === 'development',
};

// 迁移命令
// 生成迁移: pnpm run migration:generate src/migrations/MigrationName
// 执行迁移: pnpm run migration:run
// 回滚迁移: pnpm run migration:revert

API文档集成

Swagger集成

安装Swagger依赖:

pnpm install @nestjs/swagger swagger-ui-express

main.ts中配置Swagger:

// main.ts (完整配置)
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';

const bootstrap = async (): Promise<void> => {
  const app = await NestFactory.create(AppModule);
  
  // 全局验证管道
  app.useGlobalPipes(new ValidationPipe({
    whitelist: true,
    forbidNonWhitelisted: true,
    transform: true,
  }));
  
  // Swagger配置
  const config = new DocumentBuilder()
    .setTitle('NestJS API')
    .setDescription('NestJS示例项目API文档')
    .setVersion('1.0')
    .addBearerAuth()
    .addTag('用户管理', '用户相关接口')
    .build();
    
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api/docs', app, document);
  
  await app.listen(3000);
  console.log('应用启动成功: http://localhost:3000');
  console.log('API文档地址: http://localhost:3000/api/docs');
};

bootstrap();

API版本控制

实现API版本管理,支持多版本并存:

// 版本控制策略配置
import { VersioningType } from '@nestjs/common';

// 在main.ts中启用版本控制
const bootstrap = async (): Promise<void> => {
  const app = await NestFactory.create(AppModule);
  
  // 启用API版本控制
  app.enableVersioning({
    type: VersioningType.URI, // URI版本控制: /v1/users
    // type: VersioningType.HEADER, // Header版本控制: X-API-Version: 1
    // type: VersioningType.MEDIA_TYPE, // Media Type版本控制: Accept: application/json;v=1
    defaultVersion: '1',
  });
  
  await app.listen(3000);
};

// V1版本用户控制器
@Controller({
  path: 'users',
  version: '1',
})
@ApiTags('用户管理 V1')
export class UsersV1Controller {
  constructor(private readonly usersService: UsersService) {}

  /**
   * 获取用户列表 (V1)
   * @param query 查询参数
   */
  @Get()
  @Version('1')
  @ApiOperation({ summary: '获取用户列表 (V1版本)' })
  @ApiResponse({ status: 200, description: '用户列表' })
  async findAll(@Query() query: any): Promise<any[]> {
    // V1版本的简单实现
    return this.usersService.findAll();
  }

  /**
   * 获取用户详情 (V1)
   * @param id 用户ID
   */
  @Get(':id')
  @Version('1')
  @ApiOperation({ summary: '获取用户详情 (V1版本)' })
  async findOne(@Param('id') id: string): Promise<any> {
    const user = await this.usersService.findOne(+id);
    
    // V1版本返回基础字段
    return {
      id: user.id,
      name: user.name,
      email: user.email,
    };
  }
}

// V2版本用户控制器
@Controller({
  path: 'users',
  version: '2',
})
@ApiTags('用户管理 V2')
export class UsersV2Controller {
  constructor(private readonly usersService: UsersService) {}

  /**
   * 获取用户列表 (V2)
   * @param query 查询参数
   */
  @Get()
  @Version('2')
  @ApiOperation({ summary: '获取用户列表 (V2版本)' })
  @ApiResponse({ status: 200, description: '用户列表' })
  async findAll(
    @Query() query: PaginationDto,
  ): Promise<{ data: any[]; total: number; page: number }> {
    // V2版本支持分页
    const { page = 1, limit = 10 } = query;
    const [data, total] = await this.usersService.findAndCount({
      skip: (page - 1) * limit,
      take: limit,
    });
    
    return {
      data,
      total,
      page: +page,
    };
  }

  /**
   * 获取用户详情 (V2)
   * @param id 用户ID
   */
  @Get(':id')
  @Version('2')
  @ApiOperation({ summary: '获取用户详情 (V2版本)' })
  async findOne(@Param('id') id: string): Promise<any> {
    const user = await this.usersService.findOne(+id);
    
    // V2版本返回完整字段
    return {
      id: user.id,
      name: user.name,
      email: user.email,
      role: user.role,
      isActive: user.isActive,
      avatar: user.avatar,
      phone: user.phone,
      createdAt: user.createdAt,
      updatedAt: user.updatedAt,
    };
  }
}

// 版本兼容性装饰器
export const ApiVersions = (...versions: string[]) => {
  return applyDecorators(
    ...versions.map(version => Version(version)),
  );
};

// 使用示例
@Controller('products')
export class ProductsController {
  /**
   * 支持多个版本的接口
   */
  @Get()
  @ApiVersions('1', '2')
  @ApiOperation({ summary: '获取产品列表 (支持V1和V2)' })
  async findAll(@Version() version: string): Promise<any> {
    if (version === '1') {
      // V1版本逻辑
      return this.getProductsV1();
    } else {
      // V2版本逻辑
      return this.getProductsV2();
    }
  }

  private getProductsV1(): any {
    // V1版本实现
    return { version: 'v1', data: [] };
  }

  private getProductsV2(): any {
    // V2版本实现
    return { version: 'v2', data: [], meta: {} };
  }
}

// 版本弃用警告中间件
@Injectable()
export class VersionDeprecationMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void): void {
    const version = req.headers['x-api-version'] || 
                   req.url.match(/\/v(\d+)\//)?.[1] || '1';
    
    // 检查是否为弃用版本
    if (version === '1') {
      res.setHeader('X-API-Deprecated', 'true');
      res.setHeader('X-API-Sunset', '2024-12-31');
      res.setHeader('X-API-Migration-Guide', 'https://api.example.com/migration/v1-to-v2');
    }
    
    next();
  }
}

// 分页DTO
export class PaginationDto {
  @ApiPropertyOptional({ description: '页码', default: 1 })
  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  page?: number = 1;

  @ApiPropertyOptional({ description: '每页数量', default: 10 })
  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  @Max(100)
  limit?: number = 10;
}

限流和防护功能

实现API限流、防护和安全措施:

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
import { SetMetadata } from '@nestjs/common';

// 限流配置
// 在app.module.ts中配置
// ThrottlerModule.forRoot({
//   ttl: 60, // 时间窗口(秒)
//   limit: 10, // 限制次数
// })

// 自定义限流装饰器
export const Throttle = (limit: number, ttl: number) =>
  SetMetadata('throttle', { limit, ttl });

// 自定义限流守卫
@Injectable()
export class CustomThrottlerGuard extends ThrottlerGuard {
  /**
   * 获取限流配置
   * @param context 执行上下文
   */
  protected getTracker(req: Record<string, any>): string {
    // 基于IP和用户ID的限流
    const userId = req.user?.id;
    const ip = req.ip;
    return userId ? `user-${userId}` : `ip-${ip}`;
  }

  /**
   * 生成限流错误消息
   * @param context 执行上下文
   */
  protected generateErrorMessage(): string {
    return '请求过于频繁,请稍后再试';
  }
}

// IP白名单守卫
@Injectable()
export class IpWhitelistGuard implements CanActivate {
  private readonly whitelist = [
    '127.0.0.1',
    '::1',
    '192.168.1.0/24', // 支持CIDR格式
  ];

  /**
   * 检查IP是否在白名单中
   * @param context 执行上下文
   */
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const clientIp = this.getClientIp(request);
    
    return this.isIpAllowed(clientIp);
  }

  /**
   * 获取客户端真实IP
   * @param request 请求对象
   */
  private getClientIp(request: any): string {
    return (
      request.headers['x-forwarded-for']?.split(',')[0] ||
      request.headers['x-real-ip'] ||
      request.connection.remoteAddress ||
      request.socket.remoteAddress ||
      request.ip
    );
  }

  /**
   * 检查IP是否被允许
   * @param ip 客户端IP
   */
  private isIpAllowed(ip: string): boolean {
    return this.whitelist.some(allowedIp => {
      if (allowedIp.includes('/')) {
        // CIDR格式检查
        return this.isIpInCidr(ip, allowedIp);
      }
      return ip === allowedIp;
    });
  }

  /**
   * 检查IP是否在CIDR范围内
   * @param ip 客户端IP
   * @param cidr CIDR格式的IP范围
   */
  private isIpInCidr(ip: string, cidr: string): boolean {
    // 简化的CIDR检查实现
    const [network, prefixLength] = cidr.split('/');
    // 实际项目中建议使用专门的库如 'ip' 或 'netmask'
    return ip.startsWith(network.split('.').slice(0, Math.floor(+prefixLength / 8)).join('.'));
  }
}

// 请求大小限制中间件
@Injectable()
export class RequestSizeLimitMiddleware implements NestMiddleware {
  private readonly maxSize = 10 * 1024 * 1024; // 10MB

  /**
   * 检查请求大小
   * @param req 请求对象
   * @param res 响应对象
   * @param next 下一个中间件
   */
  use(req: any, res: any, next: () => void): void {
    const contentLength = parseInt(req.headers['content-length'] || '0');
    
    if (contentLength > this.maxSize) {
      res.status(413).json({
        statusCode: 413,
        message: '请求体过大',
        error: 'Payload Too Large',
      });
      return;
    }
    
    next();
  }
}

// 安全头中间件
@Injectable()
export class SecurityHeadersMiddleware implements NestMiddleware {
  /**
   * 添加安全响应头
   * @param req 请求对象
   * @param res 响应对象
   * @param next 下一个中间件
   */
  use(req: any, res: any, next: () => void): void {
    // 防止点击劫持
    res.setHeader('X-Frame-Options', 'DENY');
    
    // 防止MIME类型嗅探
    res.setHeader('X-Content-Type-Options', 'nosniff');
    
    // XSS保护
    res.setHeader('X-XSS-Protection', '1; mode=block');
    
    // 强制HTTPS
    res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
    
    // 内容安全策略
    res.setHeader('Content-Security-Policy', "default-src 'self'");
    
    // 隐藏服务器信息
    res.removeHeader('X-Powered-By');
    
    next();
  }
}

// 使用示例控制器
@Controller('protected')
@UseGuards(CustomThrottlerGuard, IpWhitelistGuard)
export class ProtectedController {
  /**
   * 高频限流接口
   */
  @Get('high-frequency')
  @Throttle(100, 60) // 每分钟100次
  @ApiOperation({ summary: '高频访问接口' })
  async highFrequency(): Promise<{ message: string }> {
    return { message: '高频访问成功' };
  }

  /**
   * 低频限流接口
   */
  @Post('low-frequency')
  @Throttle(5, 60) // 每分钟5次
  @ApiOperation({ summary: '低频访问接口' })
  async lowFrequency(@Body() data: any): Promise<{ message: string }> {
    return { message: '低频访问成功' };
  }

  /**
   * 管理员专用接口
   */
  @Get('admin-only')
  @UseGuards(IpWhitelistGuard)
  @ApiOperation({ summary: '管理员专用接口' })
  async adminOnly(): Promise<{ message: string }> {
    return { message: '管理员访问成功' };
  }
}

// 全局异常过滤器增强
@Catch()
export class SecurityExceptionFilter implements ExceptionFilter {
  /**
   * 处理安全相关异常
   * @param exception 异常对象
   * @param host 参数主机
   */
  catch(exception: any, host: ArgumentsHost): void {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();
    
    let status = 500;
    let message = '服务器内部错误';
    
    if (exception instanceof ThrottlerException) {
      status = 429;
      message = '请求过于频繁,请稍后再试';
    } else if (exception instanceof UnauthorizedException) {
      status = 401;
      message = '未授权访问';
    } else if (exception instanceof ForbiddenException) {
      status = 403;
      message = '访问被拒绝';
    }
    
    // 记录安全事件
    console.log(`Security Event: ${status} - ${request.ip} - ${request.url}`);
    
    response.status(status).json({
      statusCode: status,
      message,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}

// 在main.ts中应用中间件
// app.use(new RequestSizeLimitMiddleware().use);
// app.use(new SecurityHeadersMiddleware().use);
// app.useGlobalFilters(new SecurityExceptionFilter());

事件系统

使用EventEmitter实现应用内事件通信:

import { Injectable } from '@nestjs/common';
import { EventEmitter2, OnEvent } from '@nestjs/event-emitter';
import { Cron, CronExpression } from '@nestjs/schedule';

// 事件数据接口
export interface UserCreatedEvent {
  userId: number;
  email: string;
  name: string;
  timestamp: Date;
}

export interface OrderCompletedEvent {
  orderId: number;
  userId: number;
  amount: number;
  products: string[];
  timestamp: Date;
}

export interface SystemMaintenanceEvent {
  type: 'start' | 'end';
  message: string;
  timestamp: Date;
}

// 事件发布服务
@Injectable()
export class EventPublisherService {
  constructor(private readonly eventEmitter: EventEmitter2) {}

  /**
   * 发布用户创建事件
   * @param userData 用户数据
   */
  publishUserCreated(userData: Omit<UserCreatedEvent, 'timestamp'>): void {
    const event: UserCreatedEvent = {
      ...userData,
      timestamp: new Date(),
    };
    
    this.eventEmitter.emit('user.created', event);
  }

  /**
   * 发布订单完成事件
   * @param orderData 订单数据
   */
  publishOrderCompleted(orderData: Omit<OrderCompletedEvent, 'timestamp'>): void {
    const event: OrderCompletedEvent = {
      ...orderData,
      timestamp: new Date(),
    };
    
    this.eventEmitter.emit('order.completed', event);
  }

  /**
   * 发布系统维护事件
   * @param maintenanceData 维护数据
   */
  publishSystemMaintenance(maintenanceData: Omit<SystemMaintenanceEvent, 'timestamp'>): void {
    const event: SystemMaintenanceEvent = {
      ...maintenanceData,
      timestamp: new Date(),
    };
    
    this.eventEmitter.emit('system.maintenance', event);
  }
}

// 邮件通知监听器
@Injectable()
export class EmailNotificationListener {
  constructor(private readonly mailService: MailService) {}

  /**
   * 处理用户创建事件 - 发送欢迎邮件
   * @param event 用户创建事件
   */
  @OnEvent('user.created')
  async handleUserCreated(event: UserCreatedEvent): Promise<void> {
    console.log(`发送欢迎邮件给用户: ${event.email}`);
    
    await this.mailService.sendWelcomeEmail({
      to: event.email,
      name: event.name,
      userId: event.userId,
    });
  }

  /**
   * 处理订单完成事件 - 发送确认邮件
   * @param event 订单完成事件
   */
  @OnEvent('order.completed')
  async handleOrderCompleted(event: OrderCompletedEvent): Promise<void> {
    console.log(`发送订单确认邮件,订单ID: ${event.orderId}`);
    
    await this.mailService.sendOrderConfirmation({
      orderId: event.orderId,
      amount: event.amount,
      products: event.products,
    });
  }
}

// 数据统计监听器
@Injectable()
export class AnalyticsListener {
  constructor(private readonly analyticsService: AnalyticsService) {}

  /**
   * 处理用户创建事件 - 更新用户统计
   * @param event 用户创建事件
   */
  @OnEvent('user.created')
  async handleUserCreatedAnalytics(event: UserCreatedEvent): Promise<void> {
    console.log(`更新用户统计数据`);
    
    await this.analyticsService.incrementUserCount();
    await this.analyticsService.trackUserRegistration({
      userId: event.userId,
      timestamp: event.timestamp,
    });
  }

  /**
   * 处理订单完成事件 - 更新销售统计
   * @param event 订单完成事件
   */
  @OnEvent('order.completed')
  async handleOrderCompletedAnalytics(event: OrderCompletedEvent): Promise<void> {
    console.log(`更新销售统计数据`);
    
    await this.analyticsService.recordSale({
      amount: event.amount,
      userId: event.userId,
      timestamp: event.timestamp,
    });
  }
}

// 系统监控监听器
@Injectable()
export class SystemMonitorListener {
  constructor(private readonly logger: CustomLogger) {}

  /**
   * 处理系统维护事件
   * @param event 系统维护事件
   */
  @OnEvent('system.maintenance')
  handleSystemMaintenance(event: SystemMaintenanceEvent): void {
    this.logger.log(
      `系统维护${event.type === 'start' ? '开始' : '结束'}: ${event.message}`,
      'SystemMaintenance',
    );
    
    // 可以在这里添加更多监控逻辑
    if (event.type === 'start') {
      this.enableMaintenanceMode();
    } else {
      this.disableMaintenanceMode();
    }
  }

  /**
   * 启用维护模式
   */
  private enableMaintenanceMode(): void {
    // 实现维护模式逻辑
    console.log('启用维护模式');
  }

  /**
   * 禁用维护模式
   */
  private disableMaintenanceMode(): void {
    // 实现正常模式逻辑
    console.log('禁用维护模式');
  }
}

// 定时任务监听器
@Injectable()
export class ScheduledTasksListener {
  constructor(
    private readonly eventPublisher: EventPublisherService,
    private readonly logger: CustomLogger,
  ) {}

  /**
   * 每日系统检查
   */
  @Cron(CronExpression.EVERY_DAY_AT_2AM)
  async dailySystemCheck(): Promise<void> {
    this.logger.log('开始每日系统检查', 'ScheduledTasks');
    
    // 发布系统维护开始事件
    this.eventPublisher.publishSystemMaintenance({
      type: 'start',
      message: '每日系统检查开始',
    });
    
    // 执行系统检查逻辑
    await this.performSystemCheck();
    
    // 发布系统维护结束事件
    this.eventPublisher.publishSystemMaintenance({
      type: 'end',
      message: '每日系统检查完成',
    });
  }

  /**
   * 执行系统检查
   */
  private async performSystemCheck(): Promise<void> {
    // 模拟系统检查
    await new Promise(resolve => setTimeout(resolve, 5000));
    this.logger.log('系统检查完成', 'ScheduledTasks');
  }
}

// 使用事件的控制器示例
@Controller('events')
export class EventsController {
  constructor(private readonly eventPublisher: EventPublisherService) {}

  /**
   * 模拟用户注册
   * @param userData 用户数据
   */
  @Post('simulate-user-registration')
  @ApiOperation({ summary: '模拟用户注册事件' })
  async simulateUserRegistration(
    @Body() userData: { email: string; name: string },
  ): Promise<{ message: string }> {
    const userId = Math.floor(Math.random() * 1000) + 1;
    
    // 发布用户创建事件
    this.eventPublisher.publishUserCreated({
      userId,
      email: userData.email,
      name: userData.name,
    });
    
    return { message: '用户注册事件已发布' };
  }

  /**
   * 模拟订单完成
   * @param orderData 订单数据
   */
  @Post('simulate-order-completion')
  @ApiOperation({ summary: '模拟订单完成事件' })
  async simulateOrderCompletion(
    @Body() orderData: { userId: number; amount: number; products: string[] },
  ): Promise<{ message: string }> {
    const orderId = Math.floor(Math.random() * 10000) + 1;
    
    // 发布订单完成事件
    this.eventPublisher.publishOrderCompleted({
      orderId,
      ...orderData,
    });
    
    return { message: '订单完成事件已发布' };
  }
}

// 在app.module.ts中配置事件模块
// EventEmitterModule.forRoot({
//   wildcard: false,
//   delimiter: '.',
//   newListener: false,
//   removeListener: false,
//   maxListeners: 10,
//   verboseMemoryLeak: false,
//   ignoreErrors: false,
// })

优缺点分析

优势

  1. TypeScript原生支持:提供完整的类型安全
  2. 架构清晰:模块化设计,代码组织良好
  3. 装饰器语法:代码简洁,易于理解
  4. 丰富的功能:内置验证、序列化、缓存等功能
  5. 活跃社区:文档完善,生态丰富
  6. 企业级特性:支持微服务、测试、监控等

局限性

  1. 学习曲线:对于初学者来说概念较多
  2. 包体积:相比Express等轻量框架较大
  3. 装饰器依赖:需要启用实验性装饰器特性
  4. 过度工程:对于简单项目可能过于复杂

性能优化建议

  1. 使用Fastify:替换默认的Express适配器
  2. 启用缓存:使用内置缓存机制
  3. 数据库连接池:合理配置数据库连接
  4. 异步处理:充分利用Node.js异步特性

完整项目配置

package.json

{
  "name": "nestjs-demo",
  "version": "1.0.0",
  "description": "NestJS 完整功能示例项目",
  "scripts": {
    "build": "nest build",
    "start": "nest start",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/main",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json",
    "migration:generate": "typeorm-ts-node-commonjs migration:generate",
    "migration:run": "typeorm-ts-node-commonjs migration:run",
    "migration:revert": "typeorm-ts-node-commonjs migration:revert"
  },
  "dependencies": {
    "@nestjs/common": "^10.0.0",
    "@nestjs/core": "^10.0.0",
    "@nestjs/platform-express": "^10.0.0",
    "@nestjs/platform-socket.io": "^10.0.0",
    "@nestjs/websockets": "^10.0.0",
    "@nestjs/typeorm": "^10.0.0",
    "@nestjs/config": "^3.0.0",
    "@nestjs/swagger": "^7.0.0",
    "@nestjs/jwt": "^10.0.0",
    "@nestjs/passport": "^10.0.0",
    "@nestjs/schedule": "^4.0.0",
    "@nestjs/cache-manager": "^2.0.0",
    "@nestjs/graphql": "^12.0.0",
    "@nestjs/apollo": "^12.0.0",
    "@nestjs/microservices": "^10.0.0",
    "@nestjs/terminus": "^10.0.0",
    "typeorm": "^0.3.17",
    "mysql2": "^3.6.0",
    "class-validator": "^0.14.0",
    "class-transformer": "^0.5.1",
    "swagger-ui-express": "^5.0.0",
    "passport": "^0.6.0",
    "passport-jwt": "^4.0.1",
    "passport-local": "^1.0.0",
    "bcrypt": "^5.1.0",
    "cache-manager": "^5.2.0",
    "apollo-server-express": "^3.12.0",
    "graphql": "^16.8.0",
    "socket.io": "^4.7.0",
    "rxjs": "^7.8.0",
    "reflect-metadata": "^0.1.13"
  },
  "devDependencies": {
    "@nestjs/cli": "^10.0.0",
    "@nestjs/schematics": "^10.0.0",
    "@nestjs/testing": "^10.0.0",
    "@types/express": "^4.17.17",
    "@types/jest": "^29.5.2",
    "@types/node": "^20.0.0",
    "@types/passport-jwt": "^3.0.9",
    "@types/passport-local": "^1.0.35",
    "@types/bcrypt": "^5.0.0",
    "@types/cache-manager": "^4.0.2",
    "@typescript-eslint/eslint-plugin": "^6.0.0",
    "@typescript-eslint/parser": "^6.0.0",
    "eslint": "^8.42.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-prettier": "^5.0.0",
    "jest": "^29.5.0",
    "prettier": "^3.0.0",
    "source-map-support": "^0.5.21",
    "supertest": "^6.3.0",
    "ts-jest": "^29.1.0",
    "ts-loader": "^9.4.3",
    "ts-node": "^10.9.1",
    "tsconfig-paths": "^4.2.0",
    "typescript": "^5.1.3"
  },
  "jest": {
    "moduleFileExtensions": [
      "js",
      "json",
      "ts"
    ],
    "rootDir": "src",
    "testRegex": ".*\\.spec\\.ts$",
    "transform": {
      "^.+\\.(t|j)s$": "ts-jest"
    },
    "collectCoverageFrom": [
      "**/*.(t|j)s"
    ],
    "coverageDirectory": "../coverage",
    "testEnvironment": "node"
  }
}

项目启动步骤

  1. 创建项目
nest new my-nestjs-app
cd my-nestjs-app
  1. 安装依赖
pnpm install @nestjs/typeorm @nestjs/config @nestjs/swagger typeorm mysql2 class-validator class-transformer
  1. 配置环境变量
cp .env.example .env
# 编辑.env文件,配置数据库连接信息
  1. 启动开发服务器
pnpm start:dev

总结

NestJS是一个功能强大、架构清晰的Node.js框架,特别适合构建大型、复杂的后端应用。它的TypeScript优先设计、装饰器语法和模块化架构使得代码更加可维护和可扩展。虽然学习曲线相对陡峭,但对于需要构建企业级应用的开发团队来说,NestJS是一个非常值得推荐的选择。

适用场景

  • 大型企业级应用开发:完整的架构支持和丰富的功能
  • 微服务架构项目:内置微服务支持和多种传输层
  • 需要强类型支持的项目:TypeScript原生支持
  • 团队协作开发项目:清晰的代码结构和规范
  • RESTful API和GraphQL服务:完整的API开发解决方案

推荐理由

  1. 现代化开发体验:TypeScript + 装饰器 + 依赖注入
  2. 完整的解决方案:从开发到部署的全套工具和最佳实践
  3. 可扩展性强:模块化设计,易于扩展和维护
  4. 社区支持好:活跃的社区和丰富的第三方插件
  5. 企业级特性:内置安全、验证、缓存、监控等功能
  6. 学习资源丰富:官方文档详细,示例代码完整

学习建议

  • 先掌握TypeScript基础和装饰器语法
  • 理解依赖注入和控制反转的概念
  • 从简单的CRUD操作开始练习
  • 逐步学习高级特性如守卫、拦截器、管道等
  • 结合实际项目进行练习

如果你正在寻找一个现代化、功能完整的Node.js后端框架,NestJS绝对值得一试!它不仅能提高开发效率,还能帮助你构建更加健壮和可维护的应用程序。