Nest: Can't resolve dependency of component (validator constraint)

Created on 28 Sep 2017  路  6Comments  路  Source: nestjs/nest

Hi, I've got a problem. I'm using class-validator and creating my custom constraint like this:

@Component()
@ValidatorConstraint({ async: true })
export class IsTakenConstraint implements ValidatorConstraintInterface {

  constructor(
    @Inject(UserRepositoryToken)
    private readonly userRepository: Repository<UserEntity>,
  ) {}

  validate(userInfo: any, args: ValidationArguments) {
    return this.userRepository.findOne(userInfo).then(user => !!user);
  }
}

export const IsTaken = (validationOptions?: ValidationOptions) => (
  (object: object, propertyName: string) => {
    registerDecorator({
      propertyName,
      target: object.constructor,
      options: validationOptions,
      constraints: [],
      validator: IsTakenConstraint,
    });
  }
);

Then IsTaken is used as decorator in user.dto.ts file and validation takes place in Pipe. But... I'm getting an error 'Cannot read property 'findOne' of undefined' so it seems like userRepository is not being injected into constructor. But it is exported from DatabaseModule and works properly in UsersService.

There is UsersModule:

@Module({
    modules: [DatabaseModule],
    controllers: [UsersController],
    components: [
        ...userProviders,
        IsTakenConstraint,
        UsersService,
    ],
})
export class UsersModule {}

And usersProviders:

export const userProviders = [
  {
    provide: UserRepositoryToken,
    useFactory: (connection: Connection) => connection.getRepository(UserEntity),
    inject: [DbConnectionToken],
  },
];

I'm sure I'm missing something important here...

Most helpful comment

@Pesiok no issues, you can inject dependencies to both interceptors & guards, just remember to use controller or method scope (@UseGuards()). The global ones can't inject anything. and thanks!! :tada:

All 6 comments

Ok, so turned out my dependencies in IsTakenConstraint are resolved correctly but I'm using plain class without injected dependencies in IsTaken and I can't think of any clear way to implement it this way...
So rather than making custom decorator-based validator I created a custom availability-checking component that I'm injecting to UsersService.

Hi @Pesiok,
I think the best solution would be to create a guard, then just throw an HttpException (or derived one) when the user exists already. The second option is to use interceptor, but guards fit better here imo.
Thanks,
Kamil

@kamilmysliwiec I was thinking about using guards and that seemed like good choice, but then again - how can I inject components into them, for example this userRepository ? btw great project!

@Pesiok no issues, you can inject dependencies to both interceptors & guards, just remember to use controller or method scope (@UseGuards()). The global ones can't inject anything. and thanks!! :tada:

Ok, thanks once again. For future reference and people, like me for whom it was not immediately obvious:

Controller setup:

@Controller('route')
export class SassyController {
  constructor(
    private readonly thingsRepository: Repository<Things>,
  ) {}

  @Get()
  @UseGuards(SomeGuard)
  getThings() {
    return 'things';
  }
}

Guard setup:

@Guard()
export class SomeGuard implements CanActivate {
  constructor(
    @Inject(ThingsRepositoryToken)
    private readonly thingsRepository: Repository<Things>,
  ) {}

  public canActivate(request, context: ExecutionContext): boolean {
    if (this.thingsRepository.someMethod()) {
      return true;
    } else {
     return false;
   }
  }
}

Yes, it's that easy.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

2233322 picture 2233322  路  3Comments

artaommahe picture artaommahe  路  3Comments

JulianBiermann picture JulianBiermann  路  3Comments

janckerchen picture janckerchen  路  3Comments

marshall007 picture marshall007  路  3Comments