文章目录
- 前言
- 1. 最核心的几个装饰器(必须记住)
- 2. NestJS 提供的 TypeORM 集成工具(@nestjs/typeorm 包)
- 3. 常用 Repository 操作速查表
- 4. 目前主流推荐的几种写法风格(2025~2026)
- 5. 小Tips(非常实用)
- 扩展🚀🚀🚀
- 1. 迁移的核心概念(为什么要用迁移?)
- 2. 推荐的项目结构(2025~2026 最常见布局)
- 3. 现代推荐配置方式(2025-2026主流)
- 4. 常用 CLI 命令(package.json 推荐配置)
- 5. 迁移文件长什么样?(自动生成 vs 手动)
- 6. 2025-2026 最佳实践总结表
- 7. 快速诊断常见问题(2025-2026 仍然高频)
前言
在NestJS中使用TypeORM是目前最主流、最成熟的数据库解决方案之一(2026年依然如此),TypeORM 是 Node.js 生态里最强大的 TypeScript 原生 ORM。
NestJS + TypeORM 开发中最常用的工具、装饰器、模式和实用技巧:
1. 最核心的几个装饰器(必须记住)
| 装饰器 | 作用 | 常用位置 | 备注 |
|---|---|---|---|
@Entity() | 声明这是一个数据库表实体 | class | 可加参数:{ name: ‘custom_table’ } |
@PrimaryGeneratedColumn() | 自增主键(推荐) | property | 默认使用 increment |
@PrimaryColumn() | 自定义主键(uuid、手动id等) | property | — |
@Column() | 普通字段 | property | 支持几乎所有类型参数 |
@CreateDateColumn() | 自动创建时间 | property | — |
@UpdateDateColumn() | 自动更新时间 | property | — |
@DeleteDateColumn() | 软删除时间(配合软删除使用) | property | — |
@ManyToOne()/@OneToMany() | 多对一 / 一对多关系 | property | 最常用关系 |
@OneToOne() | 一对一关系 | property | 常用于用户-资料、商品-详情等 |
@ManyToMany()+@JoinTable() | 多对多关系(必须在一方写@JoinTable()) | property | 非常常见:标签、角色、权限等 |
2. NestJS 提供的 TypeORM 集成工具(@nestjs/typeorm 包)
| 工具/方法 | 用途 | 使用位置 | 推荐程度 |
|---|---|---|---|
TypeOrmModule.forRoot() | 全局数据库连接(推荐使用异步配置) | AppModule | ★★★★★ |
TypeOrmModule.forRootAsync() | 异步配置(推荐!配合 ConfigModule) | AppModule | ★★★★★ |
TypeOrmModule.forFeature([Entity]) | 注册特定实体,让模块内可注入 Repository | FeatureModule | ★★★★★ |
@InjectRepository(Entity) | 注入某个实体的 Repository | Service | ★★★★★ |
getRepositoryToken(Entity) | 手动提供自定义 Repository 时使用 | 自定义 provider | ★★★ |
目前(2025~2026)最推荐的全局配置写法:
// app.module.ts@Module({imports:[ConfigModule.forRoot({isGlobal:true}),TypeOrmModule.forRootAsync({imports:[ConfigModule],inject:[ConfigService],useFactory:(config:ConfigService)=>({type:'mysql',// 或 postgres、sqlite、mongodb...host:config.get('DB_HOST'),port:+config.get('DB_PORT'),username:config.get('DB_USERNAME'),password:config.get('DB_PASSWORD'),database:config.get('DB_DATABASE'),entities:[__dirname+'/**/*.entity.{ts,js}'],synchronize:config.get('NODE_ENV')!=='production',// 生产千万别开!logging:config.get('NODE_ENV')==='development'?['query','error']:false,timezone:'+08:00',// 很重要!中文项目建议加上}),}),// 其他模块...],})exportclassAppModule{}3. 常用 Repository 操作速查表
// 最常用的几种写法(都在 service 里)// 1. 基本 CRUDawaitthis.repo.find()// 全部awaitthis.repo.findOneBy({id})// 找一条(推荐)awaitthis.repo.findBy({status:1})// 条件查找多条awaitthis.repo.save(entity)// 新增/修改(智能判断)awaitthis.repo.remove(entity)// 删除实体awaitthis.repo.softRemove(entity)// 软删除(需有 deletedAt 字段)// 2. 带关联查询(最常用写法)awaitthis.repo.find({where:{id},relations:['user','user.profile','tags'],// 嵌套也支持loadRelationIds:true,// 只加载 id(性能优化)})// 3. QueryBuilder(复杂查询必备)this.repo.createQueryBuilder('post').leftJoinAndSelect('post.user','user').leftJoinAndSelect('post.tags','tag').where('post.status = :status',{status:1}).andWhere('tag.name = :tagName',{tagName:'nestjs'}).orderBy('post.createdAt','DESC').skip(0).take(10).getMany()// 4. 事务(非常重要!)awaitthis.dataSource.transaction(asyncmanager=>{awaitmanager.save(User,user);awaitmanager.save(Profile,profile);// ...更多操作})4. 目前主流推荐的几种写法风格(2025~2026)
| 风格 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|
| Active Record | 写起来最少代码 | 实体类变得臃肿,耦合严重 | 小型项目/快速原型 |
| Repository模式 | 最符合 NestJS 哲学,解耦最好 | 代码量稍多 | 中大型项目(最推荐) |
| QueryService | 统一查询入口,适合复杂查询 | 学习成本稍高 | 中后台系统、B 端系统 |
| Custom Repository | 最高自由度,可封装复杂业务逻辑 | 维护成本较高 | 业务非常复杂时 |
| AbstractService | 统一 CRUD 基类,减少重复代码 | 过度抽象容易翻车 | 团队规范强、实体非常多时 |
2025~2026 最主流推荐:Repository 模式 + QueryBuilder + 事务的组合拳
5. 小Tips(非常实用)
- 生产环境永远关闭
synchronize: true - 强烈建议使用迁移(migrations)
- 关联查询多的时候优先考虑
loadRelationIds+ 二次查询 - 性能敏感接口尽量少用
relations,改用QueryBuilder精确控制 - 软删除记得用
withDeleted才能查到已删除数据 - 大项目建议使用
typeorm-extension或自己封装BaseRepository
扩展🚀🚀🚀
TypeORM Migrations是目前(2026年1月)在NestJS项目中最推荐的数据库模式演进方式,尤其是在生产环境中绝对不能使用synchronize: true。
下面是目前最完整、最实用的 TypeORM 迁移指南(基于 TypeORM 0.3.x ~ 最新版本 + NestJS 10/11 生态):
1. 迁移的核心概念(为什么要用迁移?)
| 方式 | 开发阶段 | 生产环境 | 数据安全性 | 版本控制 | 团队协作 | 推荐程度 2026 |
|---|---|---|---|---|---|---|
synchronize: true | 非常方便 | 极度危险 | 经常丢数据 | 无 | 灾难 | ★☆☆☆☆ |
| 手动写 migration | 很安全 | 安全 | 最高 | 有 | 很好 | ★★★★☆ |
| 自动生成 migration | 方便+安全 | 安全 | 高(需审查) | 有 | 优秀 | ★★★★★ |
2025-2026 主流结论:开发阶段可以用 synchronize,生产必须关闭 + 使用迁移
最推荐的折中方案:自动生成 + 人工审查(90%以上的团队都这么做)
2. 推荐的项目结构(2025~2026 最常见布局)
src/ ├── database/ # 独立数据库相关 │ ├──>3. 现代推荐配置方式(2025-2026主流)创建src/database/data-source.ts(非常重要!CLI 要用它)
// src/database/data-source.tsimport{DataSource}from'typeorm';import*asdotenvfrom'dotenv';dotenv.config();exportconstAppDataSource=newDataSource({type:'postgres',// 或 mysql, mariadb, sqlite...host:process.env.DB_HOST||'localhost',port:Number(process.env.DB_PORT)||5432,username:process.env.DB_USERNAME||'postgres',password:process.env.DB_PASSWORD||'postgres',database:process.env.DB_NAME||'mydb',// 重要:生产一定用 js,已编译后的文件entities:['dist/**/*.entity{.ts,.js}'],migrations:['dist/database/migrations/*{.ts,.js}'],// 强烈建议:synchronize:false,// 生产必须 false!logging:process.env.NODE_ENV==='development',migrationsRun:false,// 不要在应用启动时自动跑(推荐手动或 pipeline 控制)// 可选但非常推荐(尤其 postgres/mysql)extra:{max:30,// 连接池大小connectionTimeoutMillis:2000,},});// 方便开发时直接用 ts-node 执行exportdefaultAppDataSource;
4. 常用 CLI 命令(package.json 推荐配置)
"scripts":{"typeorm":"ts-node ./node_modules/typeorm/cli.js --dataSource src/database/data-source.ts","typeorm:prod":"node --require tsconfig-paths/register ./node_modules/typeorm/cli.js --dataSource dist/database/data-source.js","migration:generate":"npm run typeorm migration:generate src/database/migrations/$npm_config_name","migration:create":"npm run typeorm migration:create src/database/migrations/$npm_config_name","migration:run":"npm run typeorm migration:run","migration:revert":"npm run typeorm migration:revert","migration:run:prod":"npm run typeorm:prod migration:run"}
使用方式示例:
# 生成迁移(推荐!)npmrun migration:generate --name=AddUserLastLogin# 空迁移(需要自己写复杂逻辑时)npmrun migration:create --name=ComplexDataMigration# 运行所有待执行迁移npmrun migration:run# 回滚最近的一次迁移npmrun migration:revert
5. 迁移文件长什么样?(自动生成 vs 手动)
自动生成的典型样子(最常见):
import{MigrationInterface,QueryRunner}from"typeorm";exportclassAddUserLastLogin1723456789012implementsMigrationInterface{publicasyncup(queryRunner:QueryRunner):Promise<void>{awaitqueryRunner.query(`ALTER TABLE "users" ADD "lastLogin" timestamp`);}publicasyncdown(queryRunner:QueryRunner):Promise<void>{awaitqueryRunner.query(`ALTER TABLE "users" DROP COLUMN "lastLogin"`);}}
手动迁移常见场景(自动生成做不到或不安全时):
- 大批量数据更新/转换
- 复杂列类型转换(jsonb → 拆分成多列)
- 表重命名 + 数据迁移
- 添加/删除外键约束(带级联)
- 插入/更新初始数据(部分团队会放在 migration 里)
6. 2025-2026 最佳实践总结表
实践 推荐度 说明 生产环境永远synchronize: false ★★★★★ 否则极易丢数据 使用单独的data-source.ts ★★★★★ CLI 和应用共用配置,避免重复 migration 文件放dist/里运行 ★★★★☆ 部署后才能执行(生产必须编译后的 js) 每次生成后必须人工 review ★★★★★ 自动生成有时会产生危险操作(如 drop column) 迁移 + CI/CD pipeline 执行 ★★★★☆ 推荐在 deploy 时自动跑 migration:run 不要在 migration 里依赖服务 ★★★★☆ migration 类不能使用 NestJS DI(独立执行) 复杂数据迁移考虑使用typeorm-extension或db:seeders ★★★☆☆ 分离 schema 变更和数据填充 团队约定迁移命名规范 ★★★★☆ e.g.AddUserProfileFields_20250110 永远保留down方法完整可执行 ★★★★☆ 便于测试回滚和环境重建
7. 快速诊断常见问题(2025-2026 仍然高频)
问题 原因 解决方式 生成迁移时把所有表都重新生成了 数据库和实体不匹配 / 命名大小写 检查数据库名、表名大小写,清理typeorm_metadata 迁移运行时报relation does not exist 迁移顺序错乱 删除migrations表记录 + 重新跑 生产环境迁移没执行 没编译成 js / 路径错误 使用typeorm:prod脚本 + 检查dist路径 Cannot find entityentities 路径不对 确认dist/**/*.entity.js