如何在NestJS项目中使用TypeORM外部npm包实体?
我之前也碰到过一模一样的情况!当你在NestJS主项目里尝试用外部npm包的TypeORM实体时,用路径扫描配置entities很容易踩坑,你遇到的RepositoryNotFoundError本质是TypeORM没能把AModule里用到的实体A和默认连接中注册的实体关联起来。
问题回顾
你有两个项目:
models:导出TypeORM实体的npm包core-api:NestJS主项目
一开始你在AppModule里用路径引入外部实体配置数据库连接:
@Module({ imports: [ TypeOrmModule.forRoot({ type: 'postgres', host: 'postgres', port: 5432, username: 'myuser', password: 'mypass', database: 'mydb', entities: [ __dirname + '../node_modules/something/models/src/*.entity.ts' ], synchronize: true, }), AModule ], controllers: [], providers: [], }) export class AppModule {}
然后在AModule里从npm包导入实体A并使用TypeOrmModule.forFeature([A]),结果触发了错误:
RepositoryNotFoundError: No repository for "A" was found. Looks like this entity is not registered in current "default" connection?
问题原因
用路径扫描的方式为什么不行?主要有两个关键点:
- 编译路径不匹配:你的配置用的是
.ts文件路径,但NestJS运行的是编译后的.js文件,实际运行时这个路径根本找不到对应的实体文件; - 实体实例不统一:即使路径能找到文件,TypeORM扫描到的实体和你通过
import { A } from 'something/models'导入的实体可能是两个不同的实例,导致TypeORM无法关联它们的Repository。
解决方法
方法一:直接导入实体类代替路径扫描
放弃路径扫描,直接把npm包中的实体类导入并配置到entities数组中,这样能确保TypeORM注册的实体和你后续使用的是同一个实例:
// 从npm包导入需要的实体 import { A } from 'something/models'; // 如果有多个实体,可以批量导入后取values // import * as ExternalEntities from 'something/models'; @Module({ imports: [ TypeOrmModule.forRoot({ type: 'postgres', host: 'postgres', port: 5432, username: 'myuser', password: 'mypass', database: 'mydb', entities: [A], // 多个实体用 Object.values(ExternalEntities) synchronize: true, }), AModule ], controllers: [], providers: [], }) export class AppModule {}
这样修改后,TypeORM就能正确识别并注册实体A,AModule里的forFeature也能正常关联到Repository了。
方法二:使用npm包提供的独立Nest模块
如果你的something/modelsnpm包已经封装了独立的Nest模块(这个模块内部已经注册了所有包内实体),直接导入这个模块会更优雅:
假设npm包内的模块是这样定义的:
// something/models 内部的模块示例 import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { A, B, C } from './entities'; // 包内所有实体 @Module({ imports: [TypeOrmModule.forFeature([A, B, C])], // 注册包内实体 exports: [TypeOrmModule], // 导出TypeOrmModule让主应用能使用这些实体的Repository }) export class ModelsModule {}
那在主AppModule里只需要导入这个模块,不需要在forRoot里配置entities了:
import { ModelsModule } from 'something/models'; @Module({ imports: [ TypeOrmModule.forRoot({ // 只保留数据库连接配置,去掉entities字段 type: 'postgres', host: 'postgres', port: 5432, username: 'myuser', password: 'mypass', database: 'mydb', synchronize: true, }), ModelsModule, // 导入包内的模块 AModule ], controllers: [], providers: [], }) export class AppModule {}
这种方式下,包内的实体已经被注册到TypeORM连接中,AModule里的forFeature([A])就能正常工作了。
总结
核心就是要让TypeORM明确识别到你使用的实体实例,避免路径扫描带来的不确定性。直接导入实体类或者利用包提供的封装模块,都能解决这个问题。
内容的提问来源于stack exchange,提问作者betomoretti

