欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 维修 > Nest.js全栈开发终极实践:TypeORM+微服务+Docker构建高可用企业级应用

Nest.js全栈开发终极实践:TypeORM+微服务+Docker构建高可用企业级应用

2025/3/11 23:52:00 来源:https://blog.csdn.net/weixin_49707375/article/details/146165348  浏览:    关键词:Nest.js全栈开发终极实践:TypeORM+微服务+Docker构建高可用企业级应用

文章目录

      • **第一部分:认识Nest.js与基础环境搭建**
        • **1.1 什么是Nest.js?**
        • **1.2 环境准备**
        • **1.3 创建第一个项目**
        • **1.4 启动开发服务器**
        • **1.5 核心文件解读**
      • **第二部分:基础控制器与路由**
        • **2.1 控制器的作用**
        • **2.2 创建自定义控制器**
        • **2.3 路由参数处理**
        • **2.4 状态码处理**
        • **2.5 完整示例**
        • **2.6 测试你的API**
      • **关键概念总结**
      • **第三部分:服务层与依赖注入**
        • **3.1 为什么需要服务层?**
        • **3.2 创建第一个服务**
        • **3.3 依赖注入实战**
        • **3.4 模块(Module)的作用**
        • **3.5 完整流程测试**
        • **3.6 依赖注入原理图解**
      • **核心概念对比**
      • **第四部分:数据库集成(TypeORM + PostgreSQL)**
        • **4.1 准备工作**
        • **4.2 配置数据库连接**
        • **4.3 创建第一个实体(Entity)**
        • **4.4 修改服务层操作数据库**
        • **4.5 修改用户模块**
        • **4.6 数据验证(DTO)**
        • **4.7 完整API测试**
        • **4.8 错误处理示例**
      • **数据库操作关键API**
      • **第五部分:身份认证(JWT策略)**
        • **5.1 认证方案概述**
        • **5.2 安装必要依赖**
        • **5.3 用户实体增强**
        • **5.4 配置JWT模块**
        • **5.5 实现认证服务**
        • **5.6 创建策略守卫**
        • **5.7 实现认证控制器**
        • **5.8 保护路由**
        • **5.9 测试认证流程**
        • **5.10 安全增强建议**
      • **认证核心组件**
      • **第六部分:异常处理与日志**
        • **6.1 异常处理的重要性**
        • **6.2 创建自定义异常过滤器**
        • **6.3 全局注册过滤器**
        • **6.4 使用内置HTTP异常**
        • **6.5 日志记录配置**
        • **6.6 集成Winston日志**
        • **6.7 请求日志中间件**
        • **6.8 错误追踪集成(Sentry示例)**
        • **6.9 测试验证**
        • **6.10 最佳实践建议**
      • **异常处理核心机制**
      • **第七部分:单元测试与E2E测试**
        • **7.1 测试金字塔模型**
        • **7.2 初始化测试环境**
        • **7.3 服务层单元测试**
        • **7.4 控制器层测试**
        • **7.5 端到端测试(E2E)**
        • **7.6 运行与解读测试**
        • **7.7 测试最佳实践**
      • **测试类型对比**
      • **第八部分:部署与生产环境优化**
        • **8.1 Docker容器化部署**
        • **8.2 环境变量配置**
        • **8.3 性能优化策略**
        • **8.4 健康检查与监控**
        • **8.5 日志收集方案**
        • **8.6 部署验证**
        • **8.7 生产环境检查清单**
      • **部署架构示意图**
      • **第九部分:微服务架构进阶**
        • **9.1 微服务核心概念**
        • **9.2 创建基础微服务**
        • **9.3 实现gRPC通信**
        • **9.4 RabbitMQ消息队列集成**
        • **9.5 分布式事务处理(Saga模式示例)**
        • **9.6 微服务通信模式对比**
        • **9.7 服务发现与负载均衡**
        • **9.8 测试微服务通信**
        • **9.9 生产环境注意事项**
      • **微服务架构核心组件**
      • **第十部分:前端集成与全栈实践**
        • **10.1 Swagger API文档集成**
        • **10.2 前端项目配置(以React为例)**
        • **10.3 实现登录认证流程**
        • **10.4 前端路由保护(React示例)**
        • **10.5 全栈调试技巧**
        • **10.6 部署联调配置**
      • **全栈开发关键点总结**
      • **项目完整工作流**

第一部分:认识Nest.js与基础环境搭建

1.1 什么是Nest.js?
  • Node.js后端框架(类似Express/Koa但更结构化)
  • 使用TypeScript构建(也支持JavaScript)
  • 结合了面向对象编程(OOP)+ 函数式编程(FP)
  • 内置依赖注入、模块化架构
  • 适合构建高效、可靠且易维护的服务端应用
1.2 环境准备

步骤1:安装Node.js

  • 前往官网下载LTS版本(建议v18+)
  • 安装完成后验证:
    node -v  # 显示版本号
    npm -v   # 显示版本号
    

步骤2:安装Nest CLI

npm install -g @nestjs/cli
1.3 创建第一个项目
nest new my-first-project

选择包管理器(推荐使用npm或yarn)

目录结构说明

my-first-project/
├── src/
│   ├── app.controller.ts    # 控制器(处理HTTP请求)
│   ├── app.service.ts       # 服务(业务逻辑)
│   ├── app.module.ts        # 根模块(组织应用结构)
│   └── main.ts              # 应用入口文件
1.4 启动开发服务器
cd my-first-project
npm run start:dev

访问 http://localhost:3000 看到"Hello World!"

1.5 核心文件解读

main.ts(应用入口):

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';async function bootstrap() {const app = await NestFactory.create(AppModule); // 创建应用实例await app.listen(3000); // 监听端口
}
bootstrap();

app.controller.ts(示例控制器):

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';@Controller() // 控制器装饰器
export class AppController {constructor(private readonly appService: AppService) {} // 依赖注入@Get() // 处理GET请求getHello(): string {return this.appService.getHello();}
}

第二部分:基础控制器与路由

2.1 控制器的作用
  • 负责处理客户端请求
  • 通过装饰器定义路由路径
  • 调用服务层处理业务逻辑
  • 返回HTTP响应
2.2 创建自定义控制器

步骤1:生成新控制器

nest generate controller user
# 或简写 nest g co user

生成文件 src/user/user.controller.ts

步骤2:基础路由示例

// user.controller.ts
import { Controller, Get } from '@nestjs/common';@Controller('users') // 定义路由前缀 /users
export class UserController {@Get() // 处理 /users 的GET请求findAll(): string {return 'All users';}@Get('profile') // 处理 /users/profile 的GET请求getProfile(): string {return 'User profile';}
}
2.3 路由参数处理

示例1:路径参数

@Get(':id') // 匹配 /users/123 形式的请求
findOne(@Param('id') id: string): string {return `User ID: ${id}`;
}

示例2:查询参数

@Get('search')
search(@Query('name') name: string): string {return `Searching for: ${name}`;
}

示例3:POST请求体

@Post()
create(@Body() userData: any): string {return `Created user: ${JSON.stringify(userData)}`;
}
2.4 状态码处理
  • 默认GET返回200,POST返回201
  • 手动指定状态码:
@Post()
@HttpCode(202) // 自定义状态码
createWithStatus() {return 'Created with custom status';
}
2.5 完整示例
import { Controller, Get, Post, Param, Query, Body, HttpCode } from '@nestjs/common';@Controller('users')
export class UserController {@Get()findAll(): string {return 'User list';}@Post()create(@Body() user: { name: string }): string {return `Created user: ${user.name}`;}@Get(':id')findOne(@Param('id') id: string): string {return `User ID: ${id}`;}@Get('search')search(@Query('keyword') keyword: string): string {return `Search keyword: ${keyword}`;}
}
2.6 测试你的API
  1. 使用Postman或curl测试:

    # GET请求示例
    curl http://localhost:3000/users
    curl http://localhost:3000/users/123
    curl http://localhost:3000/users/search?keyword=john# POST请求示例
    curl -X POST -H "Content-Type: application/json" -d '{"name":"Alice"}' http://localhost:3000/users
    
  2. 观察返回结果是否符合预期


关键概念总结

装饰器作用示例
@Controller定义控制器类并设置路由前缀@Controller('users')
@Get处理GET请求@Get(':id')
@Post处理POST请求@Post()
@Param获取路径参数@Param('id')
@Query获取查询参数@Query('page')
@Body获取请求体数据@Body() userData
@HttpCode设置响应状态码@HttpCode(204)

第三部分:服务层与依赖注入

3.1 为什么需要服务层?
  • 遵循单一职责原则(控制器只处理请求/响应)
  • 集中存放业务逻辑
  • 方便代码复用和测试
  • 通过依赖注入实现解耦
3.2 创建第一个服务

步骤1:生成服务文件

nest generate service user
# 或简写 nest g s user

生成文件:

  • src/user/user.service.ts(服务类)
  • src/user/user.service.spec.ts(测试文件)

步骤2:基础服务示例

// user.service.ts
import { Injectable } from '@nestjs/common';@Injectable() // 标记为可注入的类
export class UserService {private users = [{ id: 1, name: 'John' },{ id: 2, name: 'Alice' }];getAllUsers() {return this.users;}createUser(name: string) {const newUser = { id: Date.now(), name };this.users.push(newUser);return newUser;}
}
3.3 依赖注入实战

改造控制器(user.controller.ts):

import { Controller, Get, Post, Body } from '@nestjs/common';
import { UserService } from './user.service';@Controller('users')
export class UserController {constructor(private readonly userService: UserService) {} // 依赖注入@Get()getAllUsers() {return this.userService.getAllUsers();}@Post()createUser(@Body('name') name: string) {return this.userService.createUser(name);}
}
3.4 模块(Module)的作用

查看自动生成的 user.module.ts:

import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';@Module({controllers: [UserController],providers: [UserService], // 注册服务
})
export class UserModule {}

关键点:

  • 模块是Nest的组织单元
  • providers 数组注册可注入的类
  • 通过@Global()可以创建全局模块
3.5 完整流程测试
  1. 通过Postman发送请求:

    GET http://localhost:3000/users
    POST http://localhost:3000/users
    Body: { "name": "Bob" }
    
  2. 观察响应结果:

    // GET 响应
    [{ "id": 1, "name": "John" },{ "id": 2, "name": "Alice" }
    ]// POST 响应
    { "id": 1625641654845, "name": "Bob" }
    
3.6 依赖注入原理图解
+---------------+
|   Controller  |
|---------------|          +---------------+
|  constructor( |<--------|   UserService  |
|  userService) |          +---------------+
+---------------+▲| 通过@Module装饰器的providers数组注册
+---------------+
|    Module     |
|---------------|
| providers: [  |
|  UserService  |
| ]             |
+---------------+

核心概念对比

组件职责关键装饰器
控制器(Controller)处理HTTP请求/响应@Controller
服务(Service)实现业务逻辑@Injectable
模块(Module)组织应用结构,管理依赖关系@Module

接下来计划讲解:
第四部分:数据库集成(TypeORM + PostgreSQL)
包括:

  • TypeORM基本配置
  • 实体(Entity)创建
  • CRUD操作实现
  • 数据验证与DTO

第四部分:数据库集成(TypeORM + PostgreSQL)

4.1 准备工作
  1. 安装所需依赖:

    npm install @nestjs/typeorm typeorm pg
    
    • @nestjs/typeorm: Nest的TypeORM集成包
    • typeorm: ORM框架
    • pg: PostgreSQL驱动
  2. 确保已安装PostgreSQL数据库(本地或使用云服务)

4.2 配置数据库连接

修改app.module.ts

import { TypeOrmModule } from '@nestjs/typeorm';@Module({imports: [TypeOrmModule.forRoot({type: 'postgres',host: 'localhost',      // 数据库地址port: 5432,            // 数据库端口username: 'postgres',   // 数据库用户名password: 'your_password', // 数据库密码database: 'nestdemo',   // 数据库名称entities: [__dirname + '/**/*.entity{.ts,.js}'], // 自动扫描实体文件synchronize: true,      // 开发环境自动同步数据库结构(生产环境禁用!)}),UserModule,],
})
export class AppModule {}
4.3 创建第一个实体(Entity)

新建src/user/user.entity.ts

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';@Entity() // 标记为数据库实体
export class User {@PrimaryGeneratedColumn() // 自增主键id: number;@Column({ length: 50 }) // 字符串列,长度限制50name: string;@Column({ unique: true }) // 唯一约束email: string;@Column({ default: () => 'CURRENT_TIMESTAMP' }) // 默认值createdAt: Date;
}
4.4 修改服务层操作数据库

更新user.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';@Injectable()
export class UserService {constructor(@InjectRepository(User) // 注入用户实体仓库private userRepository: Repository<User>,) {}async findAll(): Promise<User[]> {return this.userRepository.find();}async create(userData: Partial<User>): Promise<User> {const newUser = this.userRepository.create(userData);return this.userRepository.save(newUser);}async findOne(id: number): Promise<User | null> {return this.userRepository.findOne({ where: { id } });}async remove(id: number): Promise<void> {await this.userRepository.delete(id);}
}
4.5 修改用户模块

更新user.module.ts

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UserService } from './user.service';
import { UserController } from './user.controller';@Module({imports: [TypeOrmModule.forFeature([User])], // 注册实体到模块controllers: [UserController],providers: [UserService],
})
export class UserModule {}
4.6 数据验证(DTO)
  1. 安装验证库:

    npm install class-validator class-transformer
    
  2. 创建src/user/dto/create-user.dto.ts

    import { IsString, IsEmail, Length } from 'class-validator';export class CreateUserDto {@IsString()@Length(2, 50)name: string;@IsEmail()email: string;
    }
    
  3. 更新控制器:

    import { Body, ValidationPipe } from '@nestjs/common';@Post()
    async create(@Body(new ValidationPipe()) createUserDto: CreateUserDto, // 自动验证
    ) {return this.userService.create(createUserDto);
    }
    
4.7 完整API测试
# 创建用户
curl -X POST -H "Content-Type: application/json" \
-d '{"name":"Lisa","email":"lisa@example.com"}' \
http://localhost:3000/users# 获取所有用户
curl http://localhost:3000/users# 获取单个用户
curl http://localhost:3000/users/1# 删除用户
curl -X DELETE http://localhost:3000/users/1
4.8 错误处理示例
// 在控制器中处理查找异常
@Get(':id')
async findOne(@Param('id') id: string) {const user = await this.userService.findOne(+id);if (!user) {throw new NotFoundException('User not found'); // 自动返回404}return user;
}

数据库操作关键API

方法作用示例
repository.find()获取所有记录userRepository.find()
repository.findOne()获取单条记录findOne({ where: { id } })
repository.save()创建/更新记录save(user)
repository.delete()删除记录delete(id)

接下来计划讲解:
第五部分:身份认证(JWT策略)
包括:

  • 用户注册/登录实现
  • Passport.js集成
  • JWT签发与验证
  • 路由守卫使用

第五部分:身份认证(JWT策略)

5.1 认证方案概述
1. 提交凭证
2. 验证成功
3. 返回Token
4. 携带Token
5. 验证Token
客户端
登录接口
签发JWT
受保护资源
返回数据
5.2 安装必要依赖
npm install @nestjs/passport passport passport-jwt @nestjs/jwt bcrypt
npm install @types/passport-jwt @types/bcrypt --save-dev
5.3 用户实体增强
// user.entity.ts
import { BeforeInsert } from 'typeorm';@Entity()
export class User {// ...其他字段@Column()password: string;@BeforeInsert() // 自动加密密码async hashPassword() {this.password = await bcrypt.hash(this.password, 10);}
}
5.4 配置JWT模块
// auth.module.ts
import { JwtModule } from '@nestjs/jwt';@Module({imports: [JwtModule.register({global: true,secret: 'your-secret-key', // 生产环境应使用环境变量signOptions: { expiresIn: '1h' },}),],// ...
})
export class AuthModule {}
5.5 实现认证服务
// auth.service.ts
import { compare } from 'bcrypt';@Injectable()
export class AuthService {constructor(@InjectRepository(User)private usersRepository: Repository<User>,private jwtService: JwtService,) {}async validateUser(email: string, pass: string): Promise<any> {const user = await this.usersRepository.findOne({ where: { email } });if (user && await compare(pass, user.password)) {const { password, ...result } = user; // 移除密码字段return result;}return null;}async login(user: User) {const payload = { email: user.email, sub: user.id };return {access_token: this.jwtService.sign(payload),};}
}
5.6 创建策略守卫
// jwt.strategy.ts
import { ExtractJwt, Strategy } from 'passport-jwt';@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, email: payload.email };}
}
5.7 实现认证控制器
// auth.controller.ts
@Controller('auth')
export class AuthController {constructor(private authService: AuthService) {}@Post('login')async login(@Body() loginDto: LoginDto) {const user = await this.authService.validateUser(loginDto.email,loginDto.password,);if (!user) {throw new UnauthorizedException('Invalid credentials');}return this.authService.login(user);}@Post('register')async register(@Body() createUserDto: CreateUserDto) {return this.authService.register(createUserDto);}
}
5.8 保护路由
// user.controller.ts
@Get('profile')
@UseGuards(AuthGuard('jwt')) // 添加守卫
async getProfile(@Request() req) {return req.user;
}
5.9 测试认证流程
# 注册用户
curl -X POST -H "Content-Type: application/json" \
-d '{"name":"test","email":"test@example.com","password":"123456"}' \
http://localhost:3000/auth/register# 登录获取Token
curl -X POST -H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"123456"}' \
http://localhost:3000/auth/login# 访问受保护资源
curl -H "Authorization: Bearer your-token" \
http://localhost:3000/users/profile
5.10 安全增强建议
  1. 使用环境变量存储密钥
  2. 实现密码强度验证
  3. 添加刷新令牌机制
  4. 设置合理的令牌有效期
  5. 记录认证日志

认证核心组件

组件作用关键方法/装饰器
JwtModuleJWT配置模块register()
JwtStrategy验证请求携带的JWTvalidate()
AuthGuard路由守卫@UseGuards(AuthGuard())
bcrypt密码哈希处理hash(), compare()
@nestjs/jwtJWT签发与验证sign(), verify()

第六部分:异常处理与日志

6.1 异常处理的重要性
成功
失败
客户端请求
应用处理
返回数据
异常过滤器
格式化错误响应
客户端接收
6.2 创建自定义异常过滤器

步骤1:生成过滤器

nest generate filter common/exceptions/http-exception

步骤2:实现过滤器逻辑

// http-exception.filter.ts
import {ExceptionFilter,Catch,ArgumentsHost,HttpException,
} from '@nestjs/common';
import { Request, Response } from 'express';@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {catch(exception: HttpException, host: ArgumentsHost) {const ctx = host.switchToHttp();const response = ctx.getResponse<Response>();const request = ctx.getRequest<Request>();const status = exception.getStatus();response.status(status).json({statusCode: status,timestamp: new Date().toISOString(),path: request.url,message: exception.message || 'Unknown error',});}
}
6.3 全局注册过滤器
// main.ts
async function bootstrap() {const app = await NestFactory.create(AppModule);app.useGlobalFilters(new HttpExceptionFilter()); // 注册全局过滤器await app.listen(3000);
}
6.4 使用内置HTTP异常
// user.controller.ts
@Get(':id')
async findOne(@Param('id') id: string) {const user = await this.userService.findOne(+id);if (!user) {throw new NotFoundException(`User ${id} not found`);}return user;
}
6.5 日志记录配置

启用默认日志

// main.ts
const app = await NestFactory.create(AppModule, {logger: ['log', 'error', 'warn', 'debug', 'verbose'],
});

自定义日志服务

// logger.service.ts
import { LoggerService } from '@nestjs/common';export class MyLogger implements LoggerService {log(message: string) {console.log(`[LOG] ${new Date().toISOString()} - ${message}`);}error(message: string, trace: string) {console.error(`[ERROR] ${new Date().toISOString()} - ${message}`, trace);}warn(message: string) {console.warn(`[WARN] ${new Date().toISOString()} - ${message}`);}
}// main.ts
const app = await NestFactory.create(AppModule, {logger: new MyLogger(),
});
6.6 集成Winston日志
  1. 安装依赖:
npm install winston @types/winston
  1. 创建日志配置文件:
// logger/logger.config.ts
import { createLogger, format, transports } from 'winston';export const winstonConfig = {level: 'info',format: format.combine(format.timestamp(),format.printf(({ timestamp, level, message }) => {return `${timestamp} [${level}]: ${message}`;})),transports: [new transports.Console(),new transports.File({ filename: 'logs/error.log', level: 'error' }),new transports.File({ filename: 'logs/combined.log' }),],
};
  1. 创建NestJS适配器:
// logger/winston.logger.ts
import { LoggerService } from '@nestjs/common';
import { createLogger, Logger } from 'winston';
import { winstonConfig } from './logger.config';export class WinstonLogger implements LoggerService {private logger: Logger;constructor() {this.logger = createLogger(winstonConfig);}log(message: string) {this.logger.info(message);}error(message: string, trace: string) {this.logger.error(`${message} - ${trace}`);}warn(message: string) {this.logger.warn(message);}debug(message: string) {this.logger.debug(message);}verbose(message: string) {this.logger.verbose(message);}
}
6.7 请求日志中间件
// logger/request.logger.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';@Injectable()
export class RequestLoggerMiddleware implements NestMiddleware {use(req: Request, res: Response, next: NextFunction) {const start = Date.now();const { method, originalUrl } = req;res.on('finish', () => {const duration = Date.now() - start;const { statusCode } = res;console.log(`[${method}] ${originalUrl} - ${statusCode} (${duration}ms)`,);});next();}
}// app.module.ts
export class AppModule implements NestModule {configure(consumer: MiddlewareConsumer) {consumer.apply(RequestLoggerMiddleware).forRoutes('*');}
}
6.8 错误追踪集成(Sentry示例)
  1. 安装依赖:
npm install @sentry/node @sentry/tracing
  1. 配置Sentry:
// sentry.config.ts
import * as Sentry from '@sentry/node';Sentry.init({dsn: 'your-dsn-here',tracesSampleRate: 1.0,
});
  1. 集成到异常过滤器:
// http-exception.filter.ts
catch(exception: HttpException, host: ArgumentsHost) {const ctx = host.switchToHttp();const request = ctx.getRequest<Request>();Sentry.captureException(exception, {extra: {path: request.url,method: request.method,body: request.body,},});// ...原有处理逻辑
}
6.9 测试验证
# 触发404错误
curl http://localhost:3000/nonexistent# 查看日志文件
tail -f logs/error.log# 预期输出示例
2023-08-20T10:00:00.000Z [ERROR] 404 - Cannot GET /nonexistent
6.10 最佳实践建议
  1. 使用不同日志级别:

    • verbose: 详细调试信息
    • debug: 调试信息
    • log: 常规日志
    • warn: 警告信息
    • error: 错误信息
  2. 日志文件管理:

    • 使用logrotate进行日志轮换
    • 敏感信息过滤
    • 按日期分割日志文件
  3. 生产环境注意事项:

    • 禁用synchronize选项
    • 设置适当的日志级别
    • 使用集中式日志系统(ELK/Splunk)

异常处理核心机制

组件作用关键方法/装饰器
ExceptionFilter捕获并处理异常@Catch()
HttpException预定义的HTTP异常new BadRequestException()
LoggerService日志接口log(), error()
Middleware记录请求信息implements NestMiddleware

第七部分:单元测试与E2E测试

7.1 测试金字塔模型
70% 20% 10% 测试类型分布 单元测试 集成测试 E2E测试
7.2 初始化测试环境

项目已内置测试配置

  • test/ 目录:存放测试相关配置
  • jest.config.js:Jest测试框架配置
  • package.json 脚本:
    {"scripts": {"test": "jest","test:watch": "jest --watch","test:cov": "jest --coverage"}
    }
    
7.3 服务层单元测试

测试目标:UserService
创建测试文件:user.service.spec.ts

import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UserService } from './user.service';// 模拟Repository
const mockRepository = {find: jest.fn(),findOne: jest.fn(),save: jest.fn(),delete: jest.fn(),
};describe('UserService', () => {let service: UserService;beforeEach(async () => {const module: TestingModule = await Test.createTestingModule({providers: [UserService,{provide: getRepositoryToken(User),useValue: mockRepository,},],}).compile();service = module.get<UserService>(UserService);});afterEach(() => {jest.clearAllMocks();});describe('findAll', () => {it('应返回用户数组', async () => {const mockUsers = [{ id: 1, name: 'Test' }];mockRepository.find.mockResolvedValue(mockUsers);const result = await service.findAll();expect(result).toEqual(mockUsers);expect(mockRepository.find).toHaveBeenCalledTimes(1);});});describe('create', () => {it('应成功创建用户', async () => {const newUser = { name: 'New' };mockRepository.save.mockResolvedValue({ id: 1, ...newUser });const result = await service.create(newUser);expect(result).toHaveProperty('id', 1);expect(mockRepository.save).toHaveBeenCalledWith(newUser);});});
});
7.4 控制器层测试

测试目标:UserController
创建测试文件:user.controller.spec.ts

import { Test, TestingModule } from '@nestjs/testing';
import { UserController } from './user.controller';
import { UserService } from './user.service';describe('UserController', () => {let controller: UserController;const mockUserService = {findAll: jest.fn().mockResolvedValue([{ id: 1 }]),create: jest.fn().mockImplementation((dto) => Promise.resolve({ id: 1, ...dto })),};beforeEach(async () => {const module: TestingModule = await Test.createTestingModule({controllers: [UserController],providers: [{provide: UserService,useValue: mockUserService,},],}).compile();controller = module.get<UserController>(UserController);});it('GET /users 应返回用户列表', async () => {await expect(controller.getAllUsers()).resolves.toEqual([{ id: 1 }]);expect(mockUserService.findAll).toHaveBeenCalled();});it('POST /users 应创建新用户', async () => {const dto = { name: 'Test' };await expect(controller.createUser(dto)).resolves.toEqual({id: 1,...dto,});expect(mockUserService.create).toHaveBeenCalledWith(dto);});
});
7.5 端到端测试(E2E)

创建测试文件:app.e2e-spec.ts

import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';describe('AppController (e2e)', () => {let app: INestApplication;beforeAll(async () => {const moduleFixture: TestingModule = await Test.createTestingModule({imports: [AppModule],}).compile();app = moduleFixture.createNestApplication();await app.init();});afterAll(async () => {await app.close();});it('/ (GET)', () => {return request(app.getHttpServer()).get('/').expect(200).expect('Hello World!');});describe('用户模块', () => {it('GET /users 应返回空数组', () => {return request(app.getHttpServer()).get('/users').expect(200).expect([]);});it('POST /users 应创建用户', async () => {const response = await request(app.getHttpServer()).post('/users').send({ name: 'E2E Test' }).expect(201);expect(response.body).toHaveProperty('id');expect(response.body.name).toBe('E2E Test');});});
});
7.6 运行与解读测试

执行测试命令

# 运行全部测试
npm run test# 开发时监听模式
npm run test:watch# 生成覆盖率报告
npm run test:cov

覆盖率报告示例

----------------|---------|----------|---------|---------|-------------------
File            | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------------|---------|----------|---------|---------|-------------------
All files       |     92  |     85   |    90   |    92   |                   user.service   |    100  |    100   |   100   |    100  |                   user.controller|     95  |     80   |    90   |     95  | 32                
----------------|---------|----------|---------|---------|-------------------
7.7 测试最佳实践
  1. 测试命名规范

    describe('场景描述', () => {it('应完成特定行为', () => {// 测试逻辑});
    });
    
  2. 测试三部曲

    • 准备(Arrange):设置测试数据和模拟
    • 执行(Act):调用被测试方法
    • 断言(Assert):验证结果
  3. 测试隔离原则

    • 每个测试用例独立运行
    • 使用beforeEach/afterEach重置状态
    • 避免测试间的依赖关系
  4. 测试数据库策略

    // 使用测试专用数据库
    TypeOrmModule.forRoot({database: 'test_db',synchronize: true,
    })
    

测试类型对比

测试类型测试范围执行速度维护成本适合场景
单元测试单个类/方法核心业务逻辑验证
集成测试模块间交互服务层与数据库交互
E2E测试完整系统流程用户操作流程验证

第八部分:部署与生产环境优化

8.1 Docker容器化部署

步骤1:创建Dockerfile

# 使用Node.js官方镜像
FROM node:18-alpine# 设置工作目录
WORKDIR /app# 复制依赖文件
COPY package*.json ./# 安装依赖(生产环境不装devDependencies)
RUN npm install --only=production# 复制项目文件
COPY . .# 构建项目(如果需要编译TypeScript)
RUN npm run build# 暴露端口
EXPOSE 3000# 启动命令
CMD ["npm", "run", "start:prod"]

步骤2:创建docker-compose.yml

version: '3.8'services:app:build: .ports:- "3000:3000"environment:- NODE_ENV=productiondepends_on:- dbdb:image: postgres:15environment:POSTGRES_USER: postgresPOSTGRES_PASSWORD: yoursecurepasswordPOSTGRES_DB: nestprodvolumes:- pgdata:/var/lib/postgresql/dataports:- "5432:5432"volumes:pgdata:

步骤3:构建并启动容器

docker-compose up -d --build
8.2 环境变量配置

步骤1:安装配置模块

npm install @nestjs/config

步骤2:创建.env文件

# .env.production
DATABASE_HOST=db
DATABASE_PORT=5432
DATABASE_USER=postgres
DATABASE_PASSWORD=yoursecurepassword
DATABASE_NAME=nestprod
JWT_SECRET=prod_secret_key

步骤3:更新app.module.ts

import { ConfigModule } from '@nestjs/config';@Module({imports: [ConfigModule.forRoot({envFilePath: `.env.${process.env.NODE_ENV}`,isGlobal: true,}),TypeOrmModule.forRootAsync({imports: [ConfigModule],useFactory: (config: ConfigService) => ({type: 'postgres',host: config.get('DATABASE_HOST'),port: config.get('DATABASE_PORT'),username: config.get('DATABASE_USER'),password: config.get('DATABASE_PASSWORD'),database: config.get('DATABASE_NAME'),entities: [__dirname + '/**/*.entity{.ts,.js}'],synchronize: false, // 生产环境必须关闭!}),inject: [ConfigService],}),],
})
8.3 性能优化策略

1. 启用压缩

// main.ts
import compression from 'compression';async function bootstrap() {const app = await NestFactory.create(AppModule);app.use(compression()); // 添加GZIP压缩
}

2. 集群模式(利用多核CPU)

// main.ts
import { clusterize } from '@nestjs/clusterize';async function bootstrap() {await clusterize({workers: process.env.NODE_ENV === 'production' ? 'max' : 1,bootstrap: async () => {const app = await NestFactory.create(AppModule);await app.listen(3000);},});
}

3. 缓存策略

// 安装缓存模块
npm install cache-manager @nestjs/cache-manager// app.module.ts
import { CacheModule } from '@nestjs/cache-manager';@Module({imports: [CacheModule.register({ttl: 60, // 缓存时间(秒)max: 1000, // 最大缓存数isGlobal: true,}),],
})
8.4 健康检查与监控

1. 添加终止信号处理

// main.ts
async function bootstrap() {const app = await NestFactory.create(AppModule);// 优雅关闭process.on('SIGTERM', () => {app.close().then(() => {console.log('Application closed');process.exit(0);});});
}

2. 集成健康检查

npm install @nestjs/terminus
// health.controller.ts
import { Controller, Get } from '@nestjs/common';
import { HealthCheckService, HealthCheck } from '@nestjs/terminus';@Controller('health')
export class HealthController {constructor(private health: HealthCheckService) {}@Get()@HealthCheck()check() {return this.health.check([]);}
}

3. Prometheus监控(可选)

npm install @nestjs/metrics prom-client
// metrics.module.ts
import { Module } from '@nestjs/common';
import { PrometheusModule } from '@nestjs/metrics';@Module({imports: [PrometheusModule.register()],
})
export class MetricsModule {}
8.5 日志收集方案

1. 生产环境日志配置

// winston.config.prod.ts
export const winstonConfig = {transports: [new transports.File({ filename: 'logs/error.log', level: 'error',maxsize: 1024 * 1024 * 10, // 10MBmaxFiles: 7 }),new transports.File({filename: 'logs/combined.log',maxsize: 1024 * 1024 * 50, // 50MBmaxFiles: 14})]
};

2. 日志查询命令

# 查看实时日志
tail -f logs/combined.log# 根据时间过滤日志
grep '2023-08-20T10' logs/error.log
8.6 部署验证
# 检查容器状态
docker ps -a# 查看应用日志
docker logs <container_id># 测试健康检查端点
curl http://localhost:3000/health# 压力测试(安装wrk)
wrk -t12 -c400 -d30s http://localhost:3000/users
8.7 生产环境检查清单
  1. 禁用synchronize: true
  2. 使用HTTPS加密通信
  3. 配置防火墙规则
  4. 设置自动备份策略
  5. 实施速率限制
  6. 定期安全扫描
  7. 监控CPU/内存使用
  8. 设置报警阈值

部署架构示意图

客户端
负载均衡器
容器实例1
容器实例2
PostgreSQL集群
备份存储
日志收集系统
监控仪表盘

第九部分:微服务架构进阶

9.1 微服务核心概念
客户端
API网关
用户服务
订单服务
支付服务
用户数据库
订单数据库
支付数据库
9.2 创建基础微服务

步骤1:安装依赖

npm install @nestjs/microservices

步骤2:创建用户服务(TCP通信)

// user-service/src/main.ts
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { UserModule } from './user.module';async function bootstrap() {const app = await NestFactory.createMicroservice<MicroserviceOptions>(UserModule,{transport: Transport.TCP,options: {host: 'localhost',port: 3001,},},);await app.listen();
}
bootstrap();

步骤3:定义用户服务接口

// shared/user.interface.ts
export interface User {id: number;name: string;
}export interface FindUserRequest {id: number;
}export interface CreateUserRequest {name: string;
}
9.3 实现gRPC通信

步骤1:定义proto文件

// proto/user.proto
syntax = "proto3";package user;service UserService {rpc FindUser (FindUserRequest) returns (User) {}rpc CreateUser (CreateUserRequest) returns (User) {}
}message FindUserRequest {int32 id = 1;
}message CreateUserRequest {string name = 1;
}message User {int32 id = 1;string name = 2;
}

步骤2:配置gRPC服务端

// user-service/src/main.ts
{transport: Transport.GRPC,options: {package: 'user',protoPath: join(__dirname, 'proto/user.proto'),url: 'localhost:50051',},
}

步骤3:实现gRPC客户端

// api-gateway/src/user.client.ts
@Client({transport: Transport.GRPC,options: {package: 'user',protoPath: join(__dirname, 'proto/user.proto'),url: 'localhost:50051',},
})
client: ClientGrpc;private userService: UserService;onModuleInit() {this.userService = this.client.getService<UserService>('UserService');
}@Get('users/:id')
async findUser(@Param('id') id: number) {return this.userService.findUser({ id });
}
9.4 RabbitMQ消息队列集成

步骤1:安装依赖

npm install @nestjs/microservices amqplib amqp-connection-manager

步骤2:配置消息生产者

// order-service/src/order.service.ts
@Injectable()
export class OrderService {constructor(@Inject('RABBITMQ_CLIENT') private readonly client: ClientProxy,) {}async createOrder(orderData: CreateOrderDto) {// 发送创建订单事件this.client.emit('order_created', orderData);return { status: 'processing' };}
}

步骤3:配置消息消费者

// payment-service/src/payment.consumer.ts
@Controller()
export class PaymentController {@EventPattern('order_created')async handleOrderCreated(data: CreateOrderDto) {// 处理支付逻辑console.log('Processing payment for order:', data);// 发送支付完成事件this.client.emit('payment_processed', {orderId: data.id,status: 'paid',});}
}
9.5 分布式事务处理(Saga模式示例)
// 订单创建Saga流程
async createOrderSaga(orderData) {try {// 1. 创建订单(Pending状态)const order = await this.orderService.createPendingOrder(orderData);// 2. 扣减库存await this.inventoryService.reserveStock(order.items);// 3. 处理支付const payment = await this.paymentService.processPayment(order);// 4. 确认订单await this.orderService.confirmOrder(order.id);return order;} catch (error) {// 补偿操作await this.orderService.cancelOrder(order.id);await this.inventoryService.releaseStock(order.items);throw error;}
}
9.6 微服务通信模式对比
模式协议适用场景优点缺点
请求-响应HTTP/REST简单查询操作简单易用同步阻塞
gRPCHTTP/2高性能内部通信高效、强类型需要proto定义
消息队列AMQP异步任务处理解耦、可靠架构复杂度增加
事件驱动Pub/Sub实时数据更新实时性高消息顺序需处理
9.7 服务发现与负载均衡

使用Consul示例配置

// 服务注册
import { ConsulService } from '@nestjs/consul';@Module({providers: [{provide: 'CONSUL_CLIENT',useFactory: () => {return new ConsulService({host: 'consul-server',port: '8500',});},},],
})

客户端负载均衡

@Client({transport: Transport.TCP,options: {serviceName: 'user-service',loadBalancer: new RoundRobinLoadBalancer(),discoverer: new ConsulDiscoverer({host: 'consul-server',port: 8500,}),},
})
9.8 测试微服务通信
// 测试gRPC服务
describe('UserService (gRPC)', () => {let client: UserServiceClient;beforeAll(async () => {const packageDefinition = await loadPackageDefinition(loadSync(join(__dirname, 'proto/user.proto')));const proto = packageDefinition.user as any;client = new proto.UserService('localhost:50051',credentials.createInsecure());});it('should return user details', (done) => {client.findUser({ id: 1 }, (err, response) => {expect(response).toEqual({ id: 1, name: 'Test User' });done();});});
});
9.9 生产环境注意事项
  1. 服务监控

    • 使用Prometheus + Grafana监控服务指标
    • 实现健康检查端点
  2. 容错处理

    // 断路器模式
    @Get('users/:id')
    @UseFilters(CircuitBreakerFilter)
    async getUser(@Param('id') id: string) {return this.userService.findUser(id);
    }
    
  3. 日志追踪

    • 集成OpenTelemetry实现分布式追踪
    • 使用唯一请求ID串联日志
  4. 安全策略

    • 服务间TLS加密通信
    • JWT认证传递
    • 速率限制

微服务架构核心组件

组件作用常用工具
API网关请求路由、聚合、认证Kong, NestJS网关
服务注册中心服务发现与管理Consul, etcd, Zookeeper
配置中心统一管理配置Spring Cloud Config
消息中间件异步通信RabbitMQ, Kafka
分布式追踪系统请求链路追踪Jaeger, Zipkin

第十部分:前端集成与全栈实践

10.1 Swagger API文档集成

步骤1:安装依赖

npm install @nestjs/swagger swagger-ui-express

步骤2:配置Swagger模块

// main.ts
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';async function bootstrap() {const app = await NestFactory.create(AppModule);const config = new DocumentBuilder().setTitle('NestJS API').setDescription('全栈开发接口文档').setVersion('1.0').addBearerAuth() // 启用JWT认证.build();const document = SwaggerModule.createDocument(app, config);SwaggerModule.setup('api-docs', app, document);await app.listen(3000);
}

步骤3:添加接口注释

// user.controller.ts
@ApiOperation({ summary: '获取用户列表' })
@ApiResponse({ status: 200, description: '返回用户数组' })
@Get()
getAllUsers() {return this.userService.findAll();
}// create-user.dto.ts
export class CreateUserDto {@ApiProperty({ example: '张三', description: '用户姓名' })@IsString()name: string;
}

访问文档:http://localhost:3000/api-docs


10.2 前端项目配置(以React为例)

步骤1:创建React应用

npx create-react-app nest-client
cd nest-client

步骤2:配置代理(解决跨域)

// package.json
{"proxy": "http://localhost:3000"
}

步骤3:安装axios

npm install axios

10.3 实现登录认证流程
// src/api/auth.js
import axios from 'axios';export const login = async (credentials) => {const response = await axios.post('/auth/login', credentials);localStorage.setItem('access_token', response.data.access_token);return response.data;
};export const getProfile = async () => {return axios.get('/users/profile', {headers: {Authorization: `Bearer ${localStorage.getItem('access_token')}`}});
};

10.4 前端路由保护(React示例)
// src/components/PrivateRoute.js
import { Navigate } from 'react-router-dom';const PrivateRoute = ({ children }) => {const isAuthenticated = !!localStorage.getItem('access_token');return isAuthenticated ? children : <Navigate to="/login" />;
};

10.5 全栈调试技巧

调试工具组合

45% 30% 20% 5% 调试工具使用率 浏览器开发者工具 Postman VS Code调试器 其他

常见问题排查

  1. 跨域问题

    // 后端启用CORS
    app.enableCors({origin: 'http://localhost:3001',methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',credentials: true,
    });
    
  2. 认证失效处理

    // 前端axios拦截器
    axios.interceptors.response.use(response => response,error => {if (error.response.status === 401) {window.location = '/login';}return Promise.reject(error);}
    );
    

10.6 部署联调配置

生产环境配置示例

# nginx配置
server {listen 80;server_name yourdomain.com;location /api {proxy_pass http://backend:3000;proxy_set_header Host $host;}location / {root /var/www/client;try_files $uri $uri/ /index.html;}
}

全栈开发关键点总结

层级技术栈关注重点
前端React/Vue + Axios状态管理、路由守卫
网关层Nginx负载均衡、HTTPS配置
后端NestJS + TypeORM业务逻辑、数据库优化
基础设施Docker + PostgreSQL容器编排、备份策略

项目完整工作流

用户 前端 后端 数据库 发起请求 API调用 查询数据 返回数据 JSON响应 渲染界面 用户 前端 后端 数据库

至此,Nest.js全栈开发系列教程已全部完成!建议学习者通过以下方式巩固知识:

  1. 开发一个完整博客系统(包含用户/文章/评论模块)
  2. 尝试部署到云平台(AWS/Aliyun)
  3. 参与开源Nest.js项目
  4. 探索NestJS官方高级特性(CLI插件、自定义装饰器等)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词