文章目录
- 前言
-
-
- [1. 最核心的几个装饰器(必须记住)](#1. 最核心的几个装饰器(必须记住))
- [2. NestJS 提供的 TypeORM 集成工具(@nestjs/typeorm 包)](#2. NestJS 提供的 TypeORM 集成工具(@nestjs/typeorm 包))
- [3. 常用 Repository 操作速查表](#3. 常用 Repository 操作速查表)
- [4. 目前主流推荐的几种写法风格(2025~2026)](#4. 目前主流推荐的几种写法风格(2025~2026))
- [5. 小Tips(非常实用)](#5. 小Tips(非常实用))
- 扩展🚀🚀🚀
-
- [1. 迁移的核心概念(为什么要用迁移?)](#1. 迁移的核心概念(为什么要用迁移?))
- [2. 推荐的项目结构(2025~2026 最常见布局)](#2. 推荐的项目结构(2025~2026 最常见布局))
- [3. 现代推荐配置方式(2025-2026主流)](#3. 现代推荐配置方式(2025-2026主流))
- [4. 常用 CLI 命令(package.json 推荐配置)](#4. 常用 CLI 命令(package.json 推荐配置))
- [5. 迁移文件长什么样?(自动生成 vs 手动)](#5. 迁移文件长什么样?(自动生成 vs 手动))
- [6. 2025-2026 最佳实践总结表](#6. 2025-2026 最佳实践总结表)
- [7. 快速诊断常见问题(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)最推荐的全局配置写法:
ts
// 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', // 很重要!中文项目建议加上
}),
}),
// 其他模块...
],
})
export class AppModule {}
3. 常用 Repository 操作速查表
ts
// 最常用的几种写法(都在 service 里)
// 1. 基本 CRUD
await this.repo.find() // 全部
await this.repo.findOneBy({ id }) // 找一条(推荐)
await this.repo.findBy({ status: 1 }) // 条件查找多条
await this.repo.save(entity) // 新增/修改(智能判断)
await this.repo.remove(entity) // 删除实体
await this.repo.softRemove(entity) // 软删除(需有 deletedAt 字段)
// 2. 带关联查询(最常用写法)
await this.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. 事务(非常重要!)
await this.dataSource.transaction(async manager => {
await manager.save(User, user);
await manager.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/ # 独立数据库相关
│ ├── data-source.ts # 独立的 DataSource(CLI + 应用共用)
│ ├── migrations/ # 迁移文件存放目录
│ │ ├── 1234567890-CreateUsersTable.ts
│ │ └── ...
│ └── seeds/ # 可选:数据填充
└── entities/
├── User.entity.ts
└── ...
3. 现代推荐配置方式(2025-2026主流)
创建 src/database/data-source.ts(非常重要!CLI 要用它)
ts
// src/database/data-source.ts
import { DataSource } from 'typeorm';
import * as dotenv from 'dotenv';
dotenv.config();
export const AppDataSource = new DataSource({
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 执行
export default AppDataSource;
4. 常用 CLI 命令(package.json 推荐配置)
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"
}
使用方式示例:
bash
# 生成迁移(推荐!)
npm run migration:generate --name=AddUserLastLogin
# 空迁移(需要自己写复杂逻辑时)
npm run migration:create --name=ComplexDataMigration
# 运行所有待执行迁移
npm run migration:run
# 回滚最近的一次迁移
npm run migration:revert
5. 迁移文件长什么样?(自动生成 vs 手动)
自动生成的典型样子(最常见):
ts
import { MigrationInterface, QueryRunner } from "typeorm";
export class AddUserLastLogin1723456789012 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE "users"
ADD "lastLogin" timestamp
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.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 entity |
entities 路径不对 | 确认 dist/**/*.entity.js |