NestJS:如何在模块外获取TypeORM Repository用于自定义验证
解决NestJS模块外工具类中调用TypeORM Repository的问题
问题根源
NestJS的依赖注入(DI)系统仅对模块注册的类(Provider、Controller、Guard等)生效。你把ValidatorConstraintInterface放在模块外的工具文件中,且未将该类纳入DI容器管理,直接注入UserRepository自然会返回undefined。
两种可行解决方案
方案1:通过TypeORM DataSource直接获取Repository
无需依赖Nest的DI,直接使用TypeORM的DataSource实例获取Repository,适合不需要DI管理的轻量场景。
示例代码(工具文件user-exists.validator.ts):
import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator'; import { DataSource } from 'typeorm'; import { User } from '../user/user.entity'; @ValidatorConstraint({ name: 'userExists', async: true }) export class UserExistsValidator implements ValidatorConstraintInterface { async validate(userId: number, args: ValidationArguments) { // 获取已初始化的DataSource实例(Nest启动后自动完成初始化) const dataSource = DataSource.getDataSourceByName('default'); const userRepository = dataSource.getRepository(User); const user = await userRepository.findOneBy({ id: userId }); return !!user; // 存在返回true,否则false } defaultMessage(args: ValidationArguments) { return `ID为${args.value}的用户不存在`; } }
注意:确保Nest应用已启动(DataSource完成初始化)后再调用该验证器,避免因数据源未就绪导致报错。
方案2:将验证器注册为全局Provider
让验证器纳入Nest的DI容器管理,通过构造函数注入Repository,这是更符合Nest规范的做法。
步骤1:配置UserModule为全局模块
在user.module.ts中导出验证器和Repository,并标记模块为全局:
import { Module, Global } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { User } from './user.entity'; import { UserExistsValidator } from '../utils/user-exists.validator'; @Global() // 全局模块,其他模块无需导入即可使用其导出的Provider @Module({ imports: [TypeOrmModule.forFeature([User])], providers: [UserExistsValidator], exports: [UserExistsValidator, TypeOrmModule], }) export class UserModule {}
步骤2:改造验证器类
在工具文件中给验证器添加@Injectable()装饰器,通过构造函数注入Repository:
import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator'; import { InjectRepository } from '@nestjs/typeorm'; import { User } from '../user/user.entity'; import { Repository } from 'typeorm'; import { Injectable } from '@nestjs/common'; @Injectable() // 标记为可被DI管理的类 @ValidatorConstraint({ name: 'userExists', async: true }) export class UserExistsValidator implements ValidatorConstraintInterface { constructor( @InjectRepository(User) private readonly userRepository: Repository<User>, ) {} async validate(userId: number, args: ValidationArguments) { const user = await this.userRepository.findOneBy({ id: userId }); return !!user; } defaultMessage(args: ValidationArguments) { return `ID为${args.value}的用户不存在`; } }
步骤3:在模块外的DTO中使用
直接在任意模块的DTO中引入验证器即可:
import { IsInt, Validate } from 'class-validator'; import { UserExistsValidator } from '../utils/user-exists.validator'; export class CreateOrderDto { @IsInt() @Validate(UserExistsValidator) // 验证用户是否存在 userId: number; // 其他字段... }
关键注意点
- 方案2中必须确保
UserModule被Nest应用加载(在AppModule的imports数组中),否则全局Provider无法生效。 - 如果不想将
UserModule设为全局,也可以在使用DTO的模块中导入UserModule,同样能让DI正常注入Repository。
内容的提问来源于stack exchange,提问作者Siddhesh Khadapkar
相关产品推荐
相关产品推荐

