NestJS 手动集成TypeORM
简介
TypeORM 是 Node.js 生态中最成熟、功能最全面的对象关系映射(ORM)框架之一。由于其本身就是使用 TypeScript 编写,因此与 NestJS 框架的集成非常自然且高效。
快速开始
1. 安装依赖
首先,需要安装 TypeORM 及其对应的数据库驱动(以 MySQL 为例):
npm install --save typeorm mysql2
💡 提示:如果您使用的是 PostgreSQL,请安装
pg
包;使用 SQLite,则安装sqlite3
。
2. 建立数据库连接
我们需要使用 TypeORM 的 DataSource
类来建立与数据库的连接。DataSource.initialize()
方法返回一个 Promise
,因此在 NestJS 中必须使用异步提供者(Async Provider) 来处理。
创建 database.providers.ts
文件:
// database.providers.ts
import { DataSource } from 'typeorm';export const databaseProviders = [{provide: 'DATA_SOURCE', // 自定义令牌,用于依赖注入useFactory: async () => {const dataSource = new DataSource({type: 'mysql', // 数据库类型host: 'localhost', // 主机地址port: 3306, // 端口username: 'root', // 用户名password: 'root', // 密码database: 'test', // 数据库名entities: [__dirname + '/../**/*.entity{.ts,.js}', // 自动加载实体文件],synchronize: true, // 开发环境可用,生产环境禁用!});return dataSource.initialize(); // 初始化连接},},
];
⚠️ 警告:
synchronize: true
会在应用启动时自动根据实体同步数据库表结构。此选项绝不能在生产环境中使用,否则可能导致生产数据丢失。💡 最佳实践:将自定义提供者定义在独立的
*.providers.ts
文件中。
3. 创建 DatabaseModule
将数据库提供者导出,以便其他模块可以使用。
创建 database.module.ts
文件:
// database.module.ts
import { Module } from '@nestjs/common';
import { databaseProviders } from './database.providers';@Module({providers: [...databaseProviders],exports: [...databaseProviders], // 导出提供者,使其可被其他模块注入
})
export class DatabaseModule {}
💡 说明:
DATA_SOURCE
是一个异步提供者。任何依赖它的服务或模块都会等待其Promise
解析完成后再被实例化,NestJS 会自动处理这种依赖延迟。
使用仓储模式(Repository Pattern)
TypeORM 支持仓储设计模式,每个实体都有其对应的仓储(Repository),用于执行数据库操作。
1. 创建实体(Entity)
首先,定义一个实体。以下以官方文档中的 Photo
实体为例:
// photo.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';@Entity()
export class Photo {@PrimaryGeneratedColumn()id: number;@Column({ length: 500 })name: string;@Column('text')description: string;@Column()filename: string;@Column('int')views: number;@Column()isPublished: boolean;
}
💡 此
Photo
实体通常位于photo
目录下,该目录代表一个PhotoModule
。
2. 创建仓储提供者
为 Photo
实体创建一个仓储提供者,该提供者依赖于前面创建的 DATA_SOURCE
。
创建 photo.providers.ts
文件:
// photo.providers.ts
import { DataSource } from 'typeorm';
import { Photo } from './photo.entity';export const photoProviders = [{provide: 'PHOTO_REPOSITORY', // 自定义令牌useFactory: (dataSource: DataSource) => dataSource.getRepository(Photo),inject: ['DATA_SOURCE'], // 声明依赖},
];
⚠️ 警告:在真实项目中,应避免使用“魔法字符串”(如
'PHOTO_REPOSITORY'
,'DATA_SOURCE'
)。建议将它们定义在独立的constants.ts
或tokens.ts
文件中。
3. 在服务中注入仓储
现在可以在 PhotoService
中通过 @Inject()
装饰器注入 Photo
仓储。
// photo.service.ts
import { Injectable, Inject } from '@nestjs/common';
import { Repository } from 'typeorm';
import { Photo } from './photo.entity';@Injectable()
export class PhotoService {constructor(@Inject('PHOTO_REPOSITORY')private photoRepository: Repository<Photo>, // 注入仓储) {}async findAll(): Promise<Photo[]> {return this.photoRepository.find(); // 使用仓储查询所有照片}// 可以添加更多方法,如 create, update, delete 等
}
4. 创建 PhotoModule
最后,将所有部分组合成一个完整的模块。
// photo.module.ts
import { Module } from '@nestjs/common';
import { DatabaseModule } from '../database/database.module';
import { photoProviders } from './photo.providers';
import { PhotoService } from './photo.service';@Module({imports: [DatabaseModule], // 导入 DatabaseModule 以获取数据库连接providers: [...photoProviders, // 注册 Photo 仓储提供者PhotoService, // 注册服务],exports: [PhotoService], // 如果其他模块需要使用此服务,需导出
})
export class PhotoModule {}
💡 提示:不要忘记将
PhotoModule
导入到根模块AppModule
中。
总结与建议
- 手动配置 vs 官方包:本文介绍的手动配置方式有助于深入理解 NestJS 的依赖注入机制和 TypeORM 的集成原理。
- 推荐方案:在实际项目中,强烈建议使用
@nestjs/typeorm
包,它提供了更简洁、更安全的集成方式,减少了样板代码。 - 生产环境:务必关闭
synchronize
选项,改用 TypeORM 的 Migration(迁移) 机制来管理数据库结构变更。