NestJS 教程

1. 项目概述

NestJS 是一个基于 TypeScript 的渐进式 Node.js 框架,用于构建高效、可靠、可扩展的服务器端应用。它结合了面向对象编程、函数式编程和响应式编程的元素,为开发者提供了一个结构清晰、可维护的开发环境。

1.1 主要特性

  • 模块化架构:基于模块、控制器、服务的清晰分层结构
  • 依赖注入:内置依赖注入系统,提高代码可测试性和可维护性
  • 装饰器支持:使用 TypeScript 装饰器简化代码
  • TypeScript 集成:全类型支持,提供更好的开发体验和代码质量
  • GraphQL 支持:内置 GraphQL 模块
  • WebSocket 支持:轻松实现实时通信
  • Microservices:支持微服务架构
  • 中间件:兼容 Express/Fastify 中间件
  • 生态系统丰富:提供大量官方和社区模块

1.2 适用场景

  • 企业级应用开发
  • 复杂的后端系统
  • 微服务架构
  • API 网关
  • 实时应用
  • 需要严格类型检查的项目

2. 安装与设置

2.1 环境要求

  • Node.js 16.0 或更高版本
  • npm 或 yarn 包管理器
  • TypeScript 4.7 或更高版本

2.2 安装步骤

方法一:使用 Nest CLI

# 全局安装 Nest CLI
npm install -g @nestjs/cli

# 创建项目
nest new my-nest-app

# 进入项目目录
cd my-nest-app

# 安装依赖
npm install

方法二:手动创建项目

# 创建项目目录
mkdir my-nest-app
cd my-nest-app

# 初始化项目
npm init -y

# 安装核心依赖
npm install @nestjs/common @nestjs/core @nestjs/platform-express reflect-metadata rxjs typescript

# 安装开发依赖
npm install --save-dev @types/node ts-node tsconfig-paths

2.3 基本项目结构

my-nest-app/
├── src/
│   ├── app.module.ts      # 根模块
│   ├── app.controller.ts  # 控制器
│   ├── app.service.ts     # 服务
│   └── main.ts            # 应用入口
├── test/
├── tsconfig.json
├── tsconfig.build.json
├── package.json
└── nest-cli.json

3. 核心概念

3.1 模块 (Module)

模块是 NestJS 应用的基本组织单位,用于组织代码结构。

// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
  exports: [],
})
export class AppModule {}

3.2 控制器 (Controller)

控制器负责处理 HTTP 请求和响应,定义路由。

// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
getHello(): string {
    return this.appService.getHello();
  }
}

3.3 服务 (Service)

服务负责业务逻辑,被控制器调用。

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

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

3.4 提供者 (Provider)

提供者是可以注入到其他组件的类,包括服务、仓库、工厂等。

3.5 中间件 (Middleware)

中间件函数在请求处理管道中执行,可用于日志记录、认证等。

3.6 异常过滤器 (Exception Filter)

异常过滤器用于处理应用中的异常,提供统一的错误响应。

3.7 管道 (Pipe)

管道用于数据验证和转换,在请求到达控制器之前处理数据。

3.8 守卫 (Guard)

守卫用于权限控制,决定请求是否可以到达路由处理函数。

3.9 拦截器 (Interceptor)

拦截器用于在请求处理前后执行逻辑,如日志记录、响应转换等。

4. 基本使用

4.1 创建简单的 REST API

创建控制器

// users.controller.ts
import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

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

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

  @Get()
  findAll() {
    return this.usersService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(+id);
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(+id, updateUserDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.usersService.remove(+id);
  }
}

创建服务

// users.service.ts
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

@Injectable()
export class UsersService {
  private users = [
    { id: 1, name: 'John', email: 'john@example.com' },
    { id: 2, name: 'Jane', email: 'jane@example.com' },
  ];

  create(createUserDto: CreateUserDto) {
    const newUser = {
      id: this.users.length + 1,
      ...createUserDto,
    };
    this.users.push(newUser);
    return newUser;
  }

  findAll() {
    return this.users;
  }

  findOne(id: number) {
    return this.users.find(user => user.id === id);
  }

  update(id: number, updateUserDto: UpdateUserDto) {
    const userIndex = this.users.findIndex(user => user.id === id);
    if (userIndex !== -1) {
      this.users[userIndex] = { ...this.users[userIndex], ...updateUserDto };
      return this.users[userIndex];
    }
    return null;
  }

  remove(id: number) {
    const userIndex = this.users.findIndex(user => user.id === id);
    if (userIndex !== -1) {
      const deletedUser = this.users[userIndex];
      this.users.splice(userIndex, 1);
      return deletedUser;
    }
    return null;
  }
}

创建数据传输对象 (DTO)

// dto/create-user.dto.ts
export class CreateUserDto {
  name: string;
  email: string;
}
// dto/update-user.dto.ts
export class UpdateUserDto {
  name?: string;
  email?: string;
}

创建模块

// users.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

@Module({
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

在根模块中导入

// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';

@Module({
  imports: [UsersModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

4.2 数据库集成

安装依赖

# 安装 TypeORM 和数据库驱动
npm install @nestjs/typeorm typeorm mysql2

配置数据库连接

// app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
import { User } from './users/entities/user.entity';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'password',
      database: 'nestjs-db',
      entities: [User],
      synchronize: true, // 开发环境使用,生产环境应禁用
    }),
    UsersModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

创建实体

// users/entities/user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

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

  @Column()
  name: string;

  @Column()
  email: string;

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

修改服务使用 TypeORM

// users.service.ts
import { Injectable } 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';
import { UpdateUserDto } from './dto/update-user.dto';

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

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

  async findAll(): Promise<User[]> {
    return this.usersRepository.find();
  }

  async findOne(id: number): Promise<User> {
    return this.usersRepository.findOneBy({ id });
  }

  async update(id: number, updateUserDto: UpdateUserDto): Promise<User> {
    await this.usersRepository.update(id, updateUserDto);
    return this.usersRepository.findOneBy({ id });
  }

  async remove(id: number): Promise<void> {
    await this.usersRepository.delete(id);
  }
}

修改模块导入 TypeORM

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

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

5. 高级功能

5.1 认证与授权

安装依赖

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

创建认证模块

// auth/auth.module.ts
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { UsersModule } from '../users/users.module';
import { AuthService } from './auth.service';
import { LocalStrategy } from './strategies/local.strategy';
import { JwtStrategy } from './strategies/jwt.strategy';

@Module({
  imports: [
    UsersModule,
    PassportModule,
    JwtModule.register({
      secret: 'your-secret-key',
      signOptions: { expiresIn: '60s' },
    }),
  ],
  providers: [AuthService, LocalStrategy, JwtStrategy],
  exports: [AuthService],
})
export class AuthModule {}

创建认证服务

// 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 validateUser(username: string, password: string): Promise<any> {
    const user = await this.usersService.findOneByUsername(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),
    };
  }
}

创建策略

// auth/strategies/local.strategy.ts
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from '../auth.service';

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super();
  }

  async validate(username: string, password: string): Promise<any> {
    const user = await this.authService.validateUser(username, password);
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}
// auth/strategies/jwt.strategy.ts
import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: 'your-secret-key',
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}

创建认证控制器

// auth/auth.controller.ts
import { Controller, Request, Post, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from './auth.service';

@Controller('auth')
export class AuthController {
  constructor(private authService: AuthService) {}

  @UseGuards(AuthGuard('local'))
  @Post('login')
  async login(@Request() req) {
    return this.authService.login(req.user);
  }
}

5.2 GraphQL 集成

安装依赖

npm install @nestjs/graphql @nestjs/apollo graphql apollo-server-express

配置 GraphQL

// app.module.ts
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: 'schema.gql',
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

创建 GraphQL 解析器

// users/users.resolver.ts
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { UsersService } from './users.service';
import { User } from './entities/user.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

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

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

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

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

  @Mutation(() => User)
  updateUser(
    @Args('id', { type: () => Number }) id: number,
    @Args('updateUserInput') updateUserInput: UpdateUserDto,
  ) {
    return this.usersService.update(id, updateUserInput);
  }

  @Mutation(() => User)
  removeUser(@Args('id', { type: () => Number }) id: number) {
    return this.usersService.remove(id);
  }
}

更新模块

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

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

5.3 微服务

安装依赖

npm install @nestjs/microservices

创建微服务

// main.ts
import { NestFactory } from '@nestjs/core';
import { Transport } from '@nestjs/microservices';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.createMicroservice(AppModule, {
    transport: Transport.TCP,
    options: {
      host: 'localhost',
      port: 3001,
    },
  });
  await app.listen();
  console.log('Microservice is running');
}
bootstrap();

创建消息处理器

// users/users.controller.ts
import { Controller } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';
import { UsersService } from './users.service';

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

  @MessagePattern('getUsers')
  getUsers() {
    return this.usersService.findAll();
  }

  @MessagePattern('getUser')
  getUser(data: { id: number }) {
    return this.usersService.findOne(data.id);
  }
}

6. 实用案例

6.1 构建完整的 CRUD API

项目结构

my-nest-app/
├── src/
│   ├── app.module.ts
│   ├── app.controller.ts
│   ├── app.service.ts
│   ├── main.ts
│   ├── users/
│   │   ├── users.module.ts
│   │   ├── users.controller.ts
│   │   ├── users.service.ts
│   │   ├── users.resolver.ts
│   │   ├── entities/
│   │   │   └── user.entity.ts
│   │   ├── dto/
│   │   │   ├── create-user.dto.ts
│   │   │   └── update-user.dto.ts
│   │   └── pipes/
│   │       └── user-validation.pipe.ts
│   ├── auth/
│   │   ├── auth.module.ts
│   │   ├── auth.controller.ts
│   │   ├── auth.service.ts
│   │   └── strategies/
│   │       ├── local.strategy.ts
│   │       └── jwt.strategy.ts
│   └── common/
│       ├── filters/
│       │   └── http-exception.filter.ts
│       ├── guards/
│       │   └── auth.guard.ts
│       └── interceptors/
│           └── logging.interceptor.ts
├── test/
├── tsconfig.json
├── package.json
└── nest-cli.json

核心功能

  • 用户注册、登录、认证
  • 用户 CRUD 操作
  • 数据验证
  • 错误处理
  • 日志记录
  • GraphQL API
  • REST API

6.2 实现文件上传

安装依赖

npm install @nestjs/platform-express multer
npm install --save-dev @types/multer

创建文件上传模块

// files/files.module.ts
import { Module } from '@nestjs/common';
import { FilesController } from './files.controller';
import { FilesService } from './files.service';

@Module({
  controllers: [FilesController],
  providers: [FilesService],
})
export class FilesModule {}

创建文件上传控制器

// files/files.controller.ts
import { Controller, Post, UploadedFile, UseInterceptors } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { FilesService } from './files.service';
import { diskStorage } from 'multer';
import { extname } from 'path';

@Controller('files')
export class FilesController {
  constructor(private readonly filesService: FilesService) {}

  @Post('upload')
  @UseInterceptors(
    FileInterceptor('file', {
      storage: diskStorage({
        destination: './uploads',
        filename: (req, file, callback) => {
          const randomName = Array(32)
            .fill(null)
            .map(() => Math.round(Math.random() * 16).toString(16))
            .join('');
          callback(null, `${randomName}${extname(file.originalname)}`);
        },
      }),
    }),
  )
  uploadFile(@UploadedFile() file: Express.Multer.File) {
    return this.filesService.uploadFile(file);
  }
}

创建文件上传服务

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

@Injectable()
export class FilesService {
  uploadFile(file: Express.Multer.File) {
    return {
      filename: file.filename,
      path: file.path,
      size: file.size,
    };
  }
}

7. 性能优化

7.1 代码优化

  • 使用异步操作:避免同步阻塞
  • 合理使用缓存:缓存频繁访问的数据
  • 优化数据库查询:使用索引、分页、延迟加载
  • 避免 N+1 查询问题:使用 TypeORM 的关系预加载
  • 使用拦截器处理重复逻辑:如日志记录、响应格式化

7.2 服务器配置

  • 使用 Fastify:替代 Express 获得更好的性能
  • 启用压缩:减少传输数据大小
  • 设置合理的超时:避免请求挂起
  • 使用 HTTPS:提高安全性
  • 配置 CORS:根据需要设置跨域资源共享

7.3 微服务优化

  • 使用适当的传输方式:根据场景选择 TCP、Redis、MQTT 等
  • 实现负载均衡:使用服务发现和负载均衡
  • 合理设计服务边界:避免服务间过度依赖
  • 使用断路器模式:提高系统弹性

8. 最佳实践

8.1 代码组织

  • 模块化设计:按功能组织模块
  • 分层架构:控制器 → 服务 → 数据访问
  • 单一职责:每个类只负责一个功能
  • 依赖注入:使用构造函数注入,避免直接实例化
  • 接口分离:使用接口定义服务契约

8.2 安全措施

  • 输入验证:使用管道验证所有输入
  • 密码加密:使用 bcrypt 等库加密密码
  • 防止 SQL 注入:使用 TypeORM 或参数化查询
  • 防止 XSS 攻击:转义输出
  • 使用 HTTPS:保护传输层安全
  • 实现速率限制:防止暴力攻击
  • 使用头盔:设置安全相关的 HTTP 头

8.3 测试策略

  • 单元测试:测试单个组件
  • 集成测试:测试组件间交互
  • 端到端测试:测试完整流程
  • 使用测试替身:模拟外部依赖
  • 测试覆盖率:保持高测试覆盖率

8.4 部署建议

  • 使用环境变量:分离配置和代码
  • 容器化部署:使用 Docker
  • 持续集成/持续部署:自动化测试和部署
  • 监控和日志:使用工具监控应用状态
  • 健康检查:实现健康检查端点
  • 优雅关闭:处理进程终止信号

9. 常见问题与解决方案

9.1 依赖注入错误

问题:无法解析依赖项

解决方案

  • 确保服务已在模块的 providers 中注册
  • 确保模块已在父模块中导入
  • 检查构造函数参数类型是否正确

9.2 数据库连接问题

问题:数据库连接失败

解决方案

  • 检查数据库配置是否正确
  • 确保数据库服务正在运行
  • 检查网络连接和防火墙设置
  • 验证数据库用户权限

9.3 类型错误

问题:TypeScript 类型错误

解决方案

  • 确保类型定义正确
  • 使用可选链操作符处理可能为 null 的值
  • 为泛型提供正确的类型参数
  • 检查导入的类型是否正确

9.4 性能问题

问题:应用响应缓慢

解决方案

  • 使用性能分析工具找出瓶颈
  • 优化数据库查询
  • 实现缓存
  • 考虑使用微服务架构
  • 增加服务器资源

10. 参考资源

10.1 官方文档

10.2 学习资源

10.3 第三方模块

10.4 工具与服务

11. 总结

NestJS 是一个强大的 Node.js 框架,它结合了 TypeScript 的类型安全和现代 JavaScript 的特性,为开发者提供了一个结构清晰、可维护的开发环境。通过本教程,你应该已经掌握了 NestJS 的基本使用方法和高级功能,包括:

  • 项目创建和配置
  • 模块、控制器、服务的使用
  • 数据库集成
  • 认证与授权
  • GraphQL 支持
  • 微服务架构
  • 文件上传
  • 性能优化
  • 最佳实践

NestJS 的设计理念是 "约定优于配置",它提供了一套清晰的开发规范和工具,帮助开发者快速构建高质量的后端应用。它的模块化架构和依赖注入系统使得代码更易于测试和维护,而 TypeScript 的类型系统则提供了更好的开发体验和代码质量。

NestJS 适合从简单的个人项目到复杂的企业级应用的各种场景。结合其丰富的生态系统和活跃的社区,它可以帮助你构建高效、可靠、可扩展的服务器端应用。

随着你对 NestJS 的深入了解,你会发现它不仅是一个框架,更是一种开发哲学和方法论。它鼓励开发者编写干净、可维护的代码,遵循最佳实践,从而提高开发效率和代码质量。希望本教程对你的学习和开发有所帮助!

« 上一篇 Express.js 教程 - 基于 Node.js 的 Web 框架 下一篇 » Vite 教程 - 下一代前端构建工具