NestJS+TypeORM执行种子方法报UserEntity仓库未找到错误
问题场景
基于NestJS+TypeORM连接PostgreSQL的项目中,执行UsersSeed数据种子方法时,数据可正常写入数据库,但操作完成后抛出如下错误:
No repository for "UserEntity" was found. Looks like this entity is not registered in current "default" connection? +114ms RepositoryNotFoundError: No repository for "UserEntity" was found. Looks like this entity is not registered in current "default" connection? at RepositoryNotFoundError.TypeORMError [as constructor] (E:\Projects\...\src\error\TypeORMError.ts:7:9)
相关代码说明
- 种子方法实现:方法内通过传入的Connection实例获取
UserEntity、MediaEntity对应的仓库,执行查询、写入、更新操作,代码如下:
import { MediaEntity } from '../entities/media.entity'; import { Connection, Equal } from 'typeorm'; import { UserEntity } from '../entities/user.entity'; import { Helper } from '../services/helper'; export default async function UsersSeed(connection: Connection) { const repository = connection.getRepository(UserEntity); const data: Partial<UserEntity> = { firstName: 'tesFName', lastName: 'testLNsmr', password: 'fafafa', email: 'testmail@mail.com', createdAt: Helper.dateToUTC(new Date()) }; let user = await repository.findOne({ email: data.email }); console.log("19"); console.log(user); if (!user) { const entity = Helper.createEntity(UserEntity, data); console.log("23"); user = await repository.save(entity); console.log("25"); } else { console.log("27"); await repository.update(user.id, { firstName: data.firstName, lastName: data.lastName }); console.log("33"); } console.log("36"); const mediaRepository = connection.getRepository(MediaEntity); await mediaRepository.update({ user: Equal(null) }, { user: user }); return user; }
- 根模块配置:
app.module.ts中TypeORM通过forRootAsync异步加载配置,配置逻辑由OptionsService提供,代码如下:
@Module({ imports: [ ServeStaticModule.forRoot({ rootPath: join(__dirname, 'uploads') }), TypeOrmModule.forRootAsync({ useFactory: async (optionsService: OptionsService) => optionsService.typeOrmOptions(), imports: [OptionsModule], inject: [OptionsService] }), OptionsModule, MediaModule, AuthModule, PlaceModule, RepositoryModule, ServicesModule ], controllers: [], providers: [] })
- 实体模块配置:
src/entities/entities.module.ts中已通过TypeOrmModule.forFeature注册包含UserEntity在内的所有实体并导出,代码如下:
const entities = [ UserEntity, MediaEntity, PlaceEntity, DownloadRestrictionEntity, MediaInfoEntity ]; @Module({ imports: [TypeOrmModule.forFeature(entities)], exports: [TypeOrmModule.forFeature(entities)] }) export class EntitiesModule { }
- TypeORM连接配置:
src/options/options.service.ts中entities采用通配符匹配目录下所有.entity后缀的文件,代码如下:
@Injectable() export class OptionsService { constructor( private service: ConfigService<IEnvironmentVariables> ) { } public typeOrmOptions(): TypeOrmModuleOptions { const environment = process.env.NODE_ENV ? process.env.NODE_ENV : ''; const directory = this.directory(); return { type: 'postgres', host: this.service.get('POSTGRES_HOST'), port: this.service.get('POSTGRES_PORT'), username: this.service.get('POSTGRES_USER'), password: this.service.get('POSTGRES_PASSWORD'), database: this.service.get('POSTGRES_DATABASE'), synchronize: false, migrationsRun: this.service.get('RUN_MIGRATIONS'), keepConnectionAlive: this.isTest(), entities: [`${directory}/**/*.entity${environment ? '{.ts,.js}' : '{.js, .ts}'}`], migrationsTableName: 'migrations', migrations: [`${directory}/migrations/*.ts`], cli: { migrationsDir: 'src/migrations' } } } }
故障原因
- 实体匹配glob语法错误:entities配置项的通配符规则存在明显语法问题,非环境分支的后缀匹配
{.js, .ts}逗号后存在多余空格,不符合glob多匹配项的语法要求,导致.ts后缀的实体文件无法被正确扫描注册,开发环境ts-node运行时会出现实体注册不全的问题。
注意:glob多匹配项语法要求大括号内的选项之间仅用逗号分隔,不能加空格,否则会被识别为带空格的非法文件后缀,导致匹配失效。
- 根模块未加载实体模块:
app.module.ts的imports列表中没有引入已经编写好的EntitiesModule,导致TypeOrmModule.forFeature注册的实体仓库没有在根作用域加载,框架执行生命周期收尾逻辑时无法获取到对应仓库实例。 - 连接保活逻辑缺陷:
keepConnectionAlive配置仅在测试环境返回true,种子脚本执行完成后应用触发关闭流程会主动销毁数据库连接,此时如果还有后置逻辑(如关联字段校验、日志钩子、资源回收操作)尝试访问仓库,就会在业务操作完成后抛出仓库找不到的错误。
修复方案
- 修正实体匹配的glob规则,去掉多余的环境分支判断和大括号内的空格,直接使用兼容ts、js后缀的匹配规则,将原有entities配置替换为:
entities: [`${directory}/**/*.entity.{ts,js}`], - 在根模块
app.module.ts的imports数组中添加EntitiesModule,确保forFeature注册的所有实体仓库在全局作用域正常加载。 - 调整
keepConnectionAlive配置逻辑,种子执行、开发环境下也开启连接保活,或者在种子方法逻辑全部执行完成、所有异步操作收尾后,再主动触发连接关闭,避免连接提前销毁。 - 如果是独立运行的种子脚本,不要手动创建Connection实例,通过NestJS的应用上下文获取Connection对象,确保所有模块、实体都完成初始化注册后再执行种子逻辑。
内容的提问来源于stack exchange,提问作者Muhammad Arslan Jamshaid
相关产品推荐
相关产品推荐

