NestJS中使用TypeORM

文章目录

  • 前言
      • [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-extensiondb: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
相关推荐
Drift_Dream3 小时前
Node.js 第3课:Express.js框架入门
node.js
c***69307 小时前
node.js下载、安装、设置国内镜像源(永久)(Windows11)
node.js
全栈前端老曹7 小时前
【包管理】npm init 项目名后底层发生了什么的完整逻辑
前端·javascript·npm·node.js·json·包管理·底层原理
callJJ7 小时前
MCP配置与实战:深入理解现代开发工具链
javascript·node.js·vue·mcp·windsurf
程序员爱钓鱼8 小时前
Node.js 编程实战:测试与调试 —— 日志与监控方案
前端·后端·node.js
雪域迷影9 小时前
Node.js中使用node-redis库连接redis服务端并存储数据
数据库·redis·node.js
winfredzhang11 小时前
从零构建:基于 Node.js 的全栈视频资料管理系统开发实录
css·node.js·html·音视频·js·收藏,搜索,缩略图
遗憾随她而去.11 小时前
Webpack 面试题
前端·webpack·node.js
全栈前端老曹1 天前
【包管理】read-pkg-up 快速上手教程 - 读取最近的 package.json 文件
前端·javascript·npm·node.js·json·nrm·package.json