1. Nest框架与MySQL数据库入门指南
作为现代Node.js后端开发的主流框架,NestJS凭借其模块化设计和TypeScript支持,已成为企业级应用开发的热门选择。而MySQL作为最流行的关系型数据库之一,与Nest的整合是每个全栈开发者必须掌握的技能。本文将带你从零开始搭建Nest项目,并实现完整的MySQL数据库操作流程。
提示:本文基于Nest 9.x和MySQL 8.0环境,所有代码示例均通过TypeScript 4.7验证
1.1 环境准备与项目初始化
首先确保你的开发环境已安装:
- Node.js 16+(推荐18.x LTS版本)
- MySQL 8.0+(社区版即可)
- 任意代码编辑器(VS Code为首选)
通过以下命令创建新项目:
npm i -g @nestjs/cli nest new nest-mysql-demo cd nest-mysql-demo安装MySQL驱动和TypeORM(Nest官方推荐的ORM):
npm install @nestjs/typeorm typeorm mysql21.2 数据库连接配置
在app.module.ts中配置TypeORM连接:
import { TypeOrmModule } from '@nestjs/typeorm'; @Module({ imports: [ TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'your_password', database: 'nest_demo', entities: [__dirname + '/**/*.entity{.ts,.js}'], synchronize: true, // 开发环境可用,生产环境必须关闭 }), ], }) export class AppModule {}警告:
synchronize: true会基于实体类自动同步数据库结构,方便开发但可能造成生产环境数据丢失,务必在正式部署时设为false
2. 实体定义与CRUD实现
2.1 创建第一个实体
假设我们要开发用户管理系统,先创建用户实体:
// src/users/user.entity.ts import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column({ length: 50 }) username: string; @Column({ select: false }) // 查询时默认不返回密码 password: string; @Column({ default: true }) isActive: boolean; @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' }) createdAt: Date; }2.2 实现服务层和数据访问
创建用户模块和服务:
nest generate module users nest generate service users完善服务逻辑:
// src/users/users.service.ts import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { User } from './user.entity'; @Injectable() export class UsersService { constructor( @InjectRepository(User) private usersRepository: Repository<User>, ) {} async create(user: Partial<User>): Promise<User> { return this.usersRepository.save(user); } async findAll(): Promise<User[]> { return this.usersRepository.find(); } async findOne(id: number): Promise<User> { return this.usersRepository.findOne({ where: { id } }); } async update(id: number, updateData: Partial<User>): Promise<void> { await this.usersRepository.update(id, updateData); } async remove(id: number): Promise<void> { await this.usersRepository.delete(id); } }2.3 控制器层实现
生成控制器并实现RESTful接口:
nest generate controller users// src/users/users.controller.ts import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common'; import { UsersService } from './users.service'; import { User } from './user.entity'; @Controller('users') export class UsersController { constructor(private readonly usersService: UsersService) {} @Post() create(@Body() user: User) { return this.usersService.create(user); } @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() updateData: Partial<User>) { return this.usersService.update(+id, updateData); } @Delete(':id') remove(@Param('id') id: string) { return this.usersService.remove(+id); } }3. 高级查询与事务处理
3.1 复杂查询构建
TypeORM提供了强大的查询构建器,以下是几个实用示例:
// 在UsersService中添加方法 async findActiveUsers(): Promise<User[]> { return this.usersRepository .createQueryBuilder('user') .where('user.isActive = :isActive', { isActive: true }) .orderBy('user.createdAt', 'DESC') .getMany(); } async searchUsers(keyword: string): Promise<User[]> { return this.usersRepository .createQueryBuilder() .where('username LIKE :keyword', { keyword: `%${keyword}%` }) .orWhere('email LIKE :keyword', { keyword: `%${keyword}%` }) .getMany(); }3.2 事务处理实战
对于需要原子性操作的多表写入,必须使用事务:
async transferCredits( fromId: number, toId: number, amount: number, ): Promise<void> { await this.usersRepository.manager.transaction(async (manager) => { const fromUser = await manager.findOne(User, { where: { id: fromId } }); const toUser = await manager.findOne(User, { where: { id: toId } }); if (!fromUser || !toUser) { throw new Error('用户不存在'); } if (fromUser.credits < amount) { throw new Error('余额不足'); } fromUser.credits -= amount; toUser.credits += amount; await manager.save(User, fromUser); await manager.save(User, toUser); }); }4. 性能优化与生产环境实践
4.1 连接池配置优化
在高并发场景下,合理的连接池配置至关重要:
TypeOrmModule.forRoot({ // ...其他配置 extra: { connectionLimit: 10, // 连接池大小 waitForConnections: true, queueLimit: 0, }, }),推荐配置规则:
- 开发环境:5-10个连接
- 生产环境:按 (核心数 * 2) + 有效磁盘数 计算
4.2 索引与查询优化
为常用查询字段添加索引:
@Entity() export class User { // ... @Column({ length: 50 }) @Index('IDX_USERNAME', { unique: true }) username: string; @Column() @Index('IDX_EMAIL', { unique: true }) email: string; }查询优化技巧:
- 避免SELECT *,只查询必要字段
- 对分页查询使用
skip()和take()替代limit和offset - 复杂查询考虑使用原生SQL
4.3 生产环境部署要点
- 必须关闭
synchronize,使用迁移管理数据库变更 - 启用SSL连接(云数据库必备)
- 配置适当的连接超时时间
- 实现健康检查端点监控数据库连接状态
// 健康检查示例 @Get('health') async healthCheck() { try { await this.usersRepository.query('SELECT 1'); return { status: 'ok' }; } catch (error) { throw new HttpException( { status: 'down', error: error.message }, HttpStatus.SERVICE_UNAVAILABLE, ); } }5. 常见问题排查
5.1 连接问题速查表
| 错误现象 | 可能原因 | 解决方案 |
|---|---|---|
| ECONNREFUSED | 数据库服务未启动 | 检查MySQL服务状态 |
| ER_ACCESS_DENIED_ERROR | 认证失败 | 验证用户名/密码 |
| ER_BAD_DB_ERROR | 数据库不存在 | 创建指定数据库 |
| ETIMEDOUT | 网络问题 | 检查防火墙/安全组规则 |
5.2 性能问题诊断
- 慢查询日志分析:
-- 在MySQL中启用慢查询日志 SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1;- 使用EXPLAIN分析查询计划:
const query = this.usersRepository .createQueryBuilder('user') .where('user.isActive = :isActive', { isActive: true }) .getSql(); const explainResult = await this.usersRepository.query( `EXPLAIN ${query}`, );5.3 事务隔离级别问题
MySQL默认使用REPEATABLE READ隔离级别,可能导致幻读。对于需要更高隔离级别的操作:
async sensitiveOperation() { await this.usersRepository.manager.transaction( 'SERIALIZABLE', async (manager) => { // 事务操作 }, ); }6. 扩展实践与进阶技巧
6.1 多数据库连接配置
对于需要连接多个MySQL实例的场景:
@Module({ imports: [ TypeOrmModule.forRoot({ name: 'primary', type: 'mysql', // 主数据库配置 }), TypeOrmModule.forRoot({ name: 'secondary', type: 'mysql', // 从数据库配置 }), ], }) export class AppModule {} // 使用时指定连接 @InjectRepository(User, 'secondary') private secondaryUserRepository: Repository<User>6.2 数据迁移管理
使用TypeORM迁移工具管理数据库变更:
- 安装typeorm扩展:
npm install ts-node typeorm -D- 创建迁移:
npx typeorm migration:create src/migrations/InitSchema- 编写迁移文件:
import { MigrationInterface, QueryRunner } from 'typeorm'; export class InitSchema implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(` CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, isActive BOOLEAN DEFAULT TRUE, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); } public async down(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(`DROP TABLE users`); } }- 运行迁移:
npx typeorm migration:run6.3 软删除实现
通过装饰器轻松实现软删除:
import { DeleteDateColumn } from 'typeorm'; @Entity() export class User { // ... @DeleteDateColumn() deletedAt?: Date; } // 查询时自动过滤已删除记录 this.usersRepository.find(); // 需要查询已删除记录时 this.usersRepository.find({ withDeleted: true });7. 安全最佳实践
7.1 敏感数据保护
- 密码存储必须加密:
import * as bcrypt from 'bcrypt'; async create(user: Partial<User>): Promise<User> { const salt = await bcrypt.genSalt(); user.password = await bcrypt.hash(user.password, salt); return this.usersRepository.save(user); }- 使用select: false隐藏敏感字段
- 实现DTO层过滤返回数据
7.2 SQL注入防护
TypeORM已内置防护措施,但需注意:
- 始终使用参数化查询
- 避免直接拼接SQL
- 对用户输入进行严格验证
// 安全做法 this.usersRepository .createQueryBuilder() .where('username = :username', { username: userInput }); // 危险做法(绝对避免) this.usersRepository.query( `SELECT * FROM users WHERE username = '${userInput}'`, );7.3 连接安全配置
生产环境必须启用SSL:
TypeOrmModule.forRoot({ ssl: { rejectUnauthorized: true, ca: fs.readFileSync('/path/to/server-ca.pem').toString(), cert: fs.readFileSync('/path/to/client-cert.pem').toString(), key: fs.readFileSync('/path/to/client-key.pem').toString(), }, }),8. 测试策略
8.1 单元测试示例
使用Jest测试服务层:
describe('UsersService', () => { let service: UsersService; let repository: Repository<User>; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ UsersService, { provide: getRepositoryToken(User), useClass: Repository, }, ], }).compile(); service = module.get<UsersService>(UsersService); repository = module.get<Repository<User>>(getRepositoryToken(User)); }); it('should create a user', async () => { const userData = { username: 'test', password: '123456' }; jest.spyOn(repository, 'save').mockResolvedValueOnce({ id: 1, ...userData }); const result = await service.create(userData); expect(result).toHaveProperty('id'); expect(repository.save).toHaveBeenCalled(); }); });8.2 E2E测试配置
测试模块配置使用测试数据库:
@Module({ imports: [ TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3307, // 测试数据库端口 username: 'test', password: 'test', database: 'test_db', entities: [__dirname + '/**/*.entity{.ts,.js}'], synchronize: true, // 测试环境可以开启 }), UsersModule, ], }) export class TestModule {}9. 性能监控与日志
9.1 慢查询监控
集成TypeORM日志记录慢查询:
TypeOrmModule.forRoot({ logging: ['query'], maxQueryExecutionTime: 1000, // 超过1秒的查询记录为慢查询 }),9.2 自定义查询日志
通过订阅TypeORM事件实现高级日志:
import { Connection, QueryRunner } from 'typeorm'; @Injectable() export class DatabaseLogger implements Logger { constructor(private connection: Connection) { this.connection.subscribe((event) => { if (event.queryRunner?.data?.queryDuration > 500) { console.warn(`Slow query detected: ${event.queryRunner.query}`); } }); } }10. 项目结构优化建议
10.1 模块化组织
推荐的项目结构:
src/ ├── modules/ │ ├── users/ │ │ ├── user.entity.ts │ │ ├── users.service.ts │ │ ├── users.controller.ts │ │ ├── users.module.ts │ │ └── dto/ │ │ ├── create-user.dto.ts │ │ └── update-user.dto.ts ├── shared/ │ ├── database/ │ │ └── database.module.ts │ └── utils/ └── app.module.ts10.2 数据库连接集中管理
创建共享的数据库模块:
// src/shared/database/database.module.ts @Module({ imports: [ TypeOrmModule.forRootAsync({ useFactory: () => ({ type: 'mysql', host: process.env.DB_HOST, // ...其他配置 }), }), ], exports: [TypeOrmModule], }) export class DatabaseModule {}11. 实际项目经验分享
在大型项目中处理千万级用户表时,我们发现几个关键点:
- 分库分表策略:当单表超过500万行时,考虑按用户ID哈希分片
- 读写分离:配置多个从库分担读压力
- 缓存层:对热点数据使用Redis缓存
- 连接管理:使用连接中间件如ProxySQL管理连接池
一个实用的连接池监控代码片段:
setInterval(async () => { const pool = this.connection.driver.pool; console.log({ total: pool.totalCount, idle: pool.idleCount, waiting: pool.waitingCount, }); }, 5000);12. 调试技巧
12.1 查询日志输出
临时开启完整SQL日志:
TypeOrmModule.forRoot({ logger: 'advanced-console', logging: ['query', 'error'], }),12.2 事务调试
在事务中插入调试点:
await this.usersRepository.manager.transaction(async (manager) => { console.time('transaction'); // 业务代码 console.timeEnd('transaction'); });13. 版本兼容性说明
不同版本组合的注意事项:
| NestJS | TypeORM | MySQL驱动 | 注意事项 |
|---|---|---|---|
| 9.x | 0.3.x | mysql2 2.3+ | 推荐组合 |
| 8.x | 0.2.x | mysql2 2.2+ | 需要手动安装@nestjs/typeorm |
| 7.x | 0.2.x | mysql 2.18+ | 不支持async/await语法 |
14. 替代方案比较
14.1 Sequelize vs TypeORM
| 特性 | TypeORM | Sequelize |
|---|---|---|
| 语法风格 | 装饰器+ActiveRecord | 传统ORM接口 |
| TypeScript支持 | 原生优秀 | 需要额外类型包 |
| 关联查询 | 更直观 | 稍显复杂 |
| 社区活跃度 | 高 | 极高 |
| 迁移工具 | 内置 | 需要单独安装 |
14.2 原生MySQL驱动
对于简单场景,可以直接使用mysql2包:
import { createPool } from 'mysql2/promise'; const pool = createPool({ host: 'localhost', user: 'root', database: 'test', waitForConnections: true, connectionLimit: 10, }); async function queryUsers() { const [rows] = await pool.query('SELECT * FROM users LIMIT 10'); return rows; }15. 资源推荐
官方文档:
- NestJS TypeORM集成
- TypeORM MySQL配置
性能优化指南:
- MySQL官方优化白皮书
- 《高性能MySQL》第3版
监控工具:
- Percona Monitoring and Management
- Prometheus + Grafana MySQL仪表板
安全审计工具:
- sqlmap(用于检测注入漏洞)
- MySQL Enterprise Audit
16. 升级与迁移策略
从旧版本升级时的注意事项:
- 备份优先:执行任何升级前完整备份数据库
- 逐步升级:先升级开发环境,验证后再升级生产
- 变更日志:仔细阅读TypeORM和Nest的CHANGELOG
- 测试覆盖:确保有足够的测试用例验证关键功能
典型升级步骤:
# 1. 备份package.json # 2. 更新依赖 npm install @nestjs/typeorm@latest typeorm@latest mysql2@latest # 3. 运行测试 npm test # 4. 检查弃用警告17. 云数据库实践
连接云数据库(如AWS RDS)的特殊配置:
TypeOrmModule.forRoot({ type: 'mysql', host: process.env.RDS_HOSTNAME, port: parseInt(process.env.RDS_PORT), username: process.env.RDS_USERNAME, password: process.env.RDS_PASSWORD, database: process.env.RDS_DB_NAME, ssl: { rejectUnauthorized: true, ca: fs.readFileSync('/path/to/rds-combined-ca-bundle.pem').toString(), }, }),云环境最佳实践:
- 使用IAM数据库认证替代密码
- 配置自动扩展的连接池
- 启用数据库代理减少连接开销
- 设置适当的自动备份策略
18. 连接问题深度排查
当遇到间歇性连接断开时,可按以下步骤排查:
- 检查MySQL服务器端的
wait_timeout设置 - 验证TCP keepalive配置
- 检查网络设备(负载均衡器、防火墙)的超时设置
- 在TypeORM中配置连接验证:
TypeOrmModule.forRoot({ // ... extra: { connectionTimeout: 30000, idleTimeout: 10000, keepAliveInitialDelay: 10000, }, }),19. 批量操作优化
处理大量数据时的性能技巧:
- 使用批量插入代替循环插入:
async bulkCreate(users: User[]) { await this.usersRepository .createQueryBuilder() .insert() .into(User) .values(users) .execute(); }- 分批次处理大数据集:
const batchSize = 1000; for (let i = 0; i < total; i += batchSize) { const batch = data.slice(i, i + batchSize); await this.bulkCreate(batch); }- 禁用日志提升批量操作性能:
await this.usersRepository.manager.transaction(async (manager) => { await manager.query('SET FOREIGN_KEY_CHECKS = 0'); await manager.query('SET UNIQUE_CHECKS = 0'); await manager.query('SET AUTOCOMMIT = 0'); // 批量操作 await manager.query('SET FOREIGN_KEY_CHECKS = 1'); await manager.query('SET UNIQUE_CHECKS = 1'); await manager.query('SET AUTOCOMMIT = 1'); });20. 数据类型最佳实践
MySQL与TypeScript类型映射建议:
| MySQL类型 | TypeORM装饰器 | TypeScript类型 | 注意事项 |
|---|---|---|---|
| INT | @Column('int') | number | 主键推荐使用@PrimaryGeneratedColumn() |
| VARCHAR | @Column('varchar', { length: 255 }) | string | 根据实际需求设置长度 |
| TEXT | @Column('text') | string | 大文本内容使用 |
| BOOLEAN | @Column('boolean') | boolean | MySQL实际存储为TINYINT(1) |
| DATETIME | @Column('datetime') | Date | 推荐使用TIMESTAMP自动管理时间 |
| DECIMAL | @Column('decimal', { precision: 10, scale: 2 }) | string | 避免使用float处理精确计算 |
| JSON | @Column('json') | object | 自动序列化/反序列化 |
21. 关联关系实战
实现用户与文章的1对多关系:
// user.entity.ts @OneToMany(() => Article, (article) => article.author) articles: Article[]; // article.entity.ts @ManyToOne(() => User, (user) => user.articles) @JoinColumn({ name: 'author_id' }) author: User;查询关联数据的几种方式:
- 使用
relations选项:
this.usersRepository.find({ relations: ['articles'] });- 使用查询构建器:
this.usersRepository .createQueryBuilder('user') .leftJoinAndSelect('user.articles', 'articles') .where('user.id = :id', { id: 1 }) .getOne();- 使用
@RelationId获取外键:
@RelationId((article: Article) => article.author) authorId: number;22. 自定义仓储模式
扩展默认Repository功能:
// src/users/users.repository.ts @EntityRepository(User) export class UsersRepository extends Repository<User> { async findActiveUsers(): Promise<User[]> { return this.find({ where: { isActive: true } }); } } // 在模块中注册 @Module({ imports: [TypeOrmModule.forFeature([User, UsersRepository])], providers: [UsersService], controllers: [UsersController], }) export class UsersModule {} // 在服务中使用 @Injectable() export class UsersService { constructor( @InjectRepository(User) private usersRepository: UsersRepository, ) {} }23. 数据库健康检查
实现全面的健康检查端点:
@Get('health') async healthCheck() { const dbCheck = await this.checkDatabase(); const redisCheck = await this.checkRedis(); return { status: dbCheck && redisCheck ? 'healthy' : 'degraded', details: { database: dbCheck ? 'up' : 'down', redis: redisCheck ? 'up' : 'down', timestamp: new Date().toISOString(), }, }; } private async checkDatabase(): Promise<boolean> { try { await this.usersRepository.query('SELECT 1'); return true; } catch { return false; } }24. 连接池监控与管理
高级连接池配置示例:
TypeOrmModule.forRoot({ // ...其他配置 extra: { poolSize: 20, acquireTimeout: 30000, idleTimeout: 10000, queueLimit: 0, enableKeepAlive: true, keepAliveInitialDelay: 10000, }, }),监控连接池状态的实用方法:
@Injectable() export class DatabaseMonitorService { constructor(private connection: Connection) {} getPoolStats() { const pool = this.connection.driver.pool; return { total: pool.totalCount, idle: pool.idleCount, waiting: pool.waitingCount, max: pool.options.connectionLimit, }; } }25. 生产环境部署清单
上线前必须检查的项目:
安全配置:
- 禁用
synchronize - 启用SSL连接
- 使用强密码和最小权限账户
- 禁用
性能配置:
- 优化连接池大小
- 设置适当的查询超时
- 配置慢查询日志
监控配置:
- 实现健康检查端点
- 设置数据库性能监控
- 配置告警规则
备份策略:
- 定期全量备份
- 二进制日志增量备份
- 测试恢复流程
灾难恢复:
- 多可用区部署
- 故障转移演练
- 回滚方案准备
26. 性能基准测试
使用Jest进行简单的性能测试:
describe('Performance Tests', () => { it('should handle 100 concurrent queries', async () => { const start = Date.now(); const promises = Array(100) .fill(0) .map(() => usersService.findAll()); await Promise.all(promises); const duration = Date.now() - start; console.log(`100 queries took ${duration}ms`); expect(duration).toBeLessThan(1000); }); });关键性能指标参考值:
| 操作类型 | 单次操作预期耗时 | 并发能力 |
|---|---|---|
| 简单查询 | <10ms | 1000+ QPS |
| 复杂联查 | 50-100ms | 100+ QPS |
| 插入操作 | 20-50ms | 500+ QPS |
| 更新操作 | 30-60ms | 300+ QPS |
27. 连接泄漏排查
检测和修复连接泄漏的步骤:
- 监控连接数增长:
setInterval(() => { const pool = connection.driver.pool; console.log(`Connections: ${pool.totalCount}/${pool.options.connectionLimit}`); }, 1000);- 使用AsyncLocalStorage跟踪连接获取:
import { AsyncLocalStorage } from 'async_hooks'; const connectionStorage = new AsyncLocalStorage(); async function withConnection<T>(fn: () => Promise<T>): Promise<T> { return connectionStorage.run(new Date(), async () => { try { return await fn(); } finally { const start = connectionStorage.getStore(); if (start && Date.now() - start > 1000) { console.warn('Long running connection detected'); } } }); }- 常见泄漏场景:
- 未处理异常的异步操作
- 忘记释放的手动事务
- 循环中未关闭的游标
28. 多租户实现
使用TypeORM实现多租户架构:
// 租户感知的Repository @EntityRepository(User) export class TenantAwareRepository extends Repository<User> { async findForTenant(tenantId: string): Promise<User[]> { return this.createQueryBuilder('user') .where('user.tenantId = :tenantId', { tenantId }) .getMany(); } } // 请求作用域的租户ID @Injectable({ scope: Scope.REQUEST }) export class TenantService { tenantId: string; } // 在服务中使用 @Injectable() export class UsersService { constructor( @InjectRepository(User) private usersRepository: TenantAwareRepository, private tenantService: TenantService, ) {} async findAll(): Promise<User[]> { return this.usersRepository.findForTenant(this.tenantService.tenantId); } }29. 地理空间数据处理
MySQL支持的地理空间功能示例:
@Entity() export class Place { @PrimaryGeneratedColumn() id: number; @Column('point') location: string; @Column() name: string; } // 查询附近的点 async findNearby(lat: number, lng: number, radius: number) { return this.placesRepository .createQueryBuilder() .where( `ST_Distance_Sphere(location, ST_GeomFromText('POINT(${lng} ${lat})')) <= :radius`, { radius }, ) .getMany(); }30. 全文搜索实现
使用MySQL全文索引:
@Entity() export class Article { @PrimaryGeneratedColumn() id: number; @Column() @Index({ fulltext: true }) title: string; @Column('text') @Index({ fulltext: true }) content: string; } // 全文搜索查询 async searchArticles(keyword: string) { return this.articlesRepository .createQueryBuilder() .where( `MATCH(title, content) AGAINST(:keyword IN BOOLEAN MODE)`, { keyword: `+${keyword}*` }, ) .getMany(); }31. 数据加密方案
敏感字段加密策略:
import { encrypt, decrypt } from './crypto.util'; @Entity() export class User { @Column({ transformer: { to: (value: string) => encrypt(value), from: (value: string) => decrypt(value), }, }) privateKey: string; }加密工具示例:
// src/shared/utils/crypto.util.ts import * as crypto from 'crypto'; const algorithm = 'aes-256-cbc'; const key = crypto.scryptSync(process.env.ENCRYPTION_KEY, 'salt', 32); const iv = Buffer.alloc(16, 0); export function encrypt(text: string): string { const cipher = crypto.createCipheriv(algorithm, key, iv); let encrypted = cipher.update(text, 'utf8', 'hex'); encrypted += cipher.final('hex'); return encrypted; } export function decrypt(encrypted: string): string { const decipher = crypto.createDecipheriv(algorithm, key, iv); let decrypted = decipher.update(encrypted, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; }32. 数据库版本控制
使用Flyway管理数据库变更:
- 安装Flyway CLI
- 创建迁移脚本目录结构
- 编写SQL迁移文件:
-- V1__Initial_schema.sql CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL ); -- V2__Add_email_column.sql ALTER TABLE users ADD COLUMN email VARCHAR(255);- 集成到Nest应用生命周期:
@Injectable() export class FlywayService { async migrate() { const flyway = new Flyway({ url: process.env.DATABASE_URL, user: process.env.DATABASE_USER, password: process.env.DATABASE_PASSWORD, locations: ['filesystem:./migrations'], }); await flyway.migrate(); } } // 在main.ts中调用 async function bootstrap() { const app = await NestFactory.create(AppModule); await app.get(FlywayService).migrate(); await app.listen(3000); }33. 读写分离实战
配置读写分离连接:
TypeOrmModule.forRootAsync({ name: 'write', useFactory: () => ({ type: 'mysql', host: process.env.DB_WRITE_HOST, // ...写库配置 }), }), TypeOrmModule.forRootAsync({ name: 'read', useFactory: () => ({ type: 'mysql', host: process.env.DB_READ_HOST, // ...读库配置 }), }),使用装饰器选择数据源:
@Injectable() export class UsersService { constructor( @InjectRepository(User, 'write') private writeRepository: Repository<User>, @InjectRepository(User, 'read') private readRepository: Repository<User>, ) {} async readOperations() { return this.readRepository.find(); } async writeOperations() { return this.writeRepository.save(new User()); } }34. 连接失败重试策略
实现健壮的重连机制:
async function createConnectionWithRetry(options: ConnectionOptions, maxRetries = 3) { let retries = 0; while (retries < maxRetries) { try { return await createConnection(options); } catch (error) { retries++; if (retries === maxRetries) throw error; await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, retries)) ); console.log(`Retrying connection (attempt ${retries})...`);