Home > Back-end >  Request-scoped service undefined in custom validaor
Request-scoped service undefined in custom validaor

Time:07-20

I'm encountering trouble leveraging moduleRef in conjunction with a Request-scoped custom provider in a custom decorator. In particular, using the moduleRef.resolve() method, the scoped service is still coming in as undefined. I've been following these instructions for using Dependency Injection within the request scope to no avail.

the request-scoped service

import { Injectable, Scope } from '@nestjs/common';

@Injectable({ scope: Scope.REQUEST })
export class RequestService {
  private isCoffee: boolean;

  setIsCoffee(drink: string) {
    this.isCoffee = ['mocha', 'espresso', 'latte'].includes(drink);
  }

  getIsCoffee(): boolean {
    return this.isCoffee;
  }
}

request.module.ts

import { Module } from '@nestjs/common';
import { RequestService } from './request.service';

@Module({
  exports: [RequestService],
  providers: [RequestService],
})
export class RequestModule {}

custom decorator

import {/*...*/}

@ValidatorConstraint({ name: 'IsCoffeeValidator', async: true })
@Injectable()
export class IsCoffeeValidator
  implements ValidatorConstraintInterface, OnModuleInit
{
  private requestService: RequestService;
  constructor(
    @Inject(REQUEST) private request: Request,
    // @Inject(RequestService) private requestService: RequestService, // A normal DI doesn't work
    private moduleRef: ModuleRef,
  ) {}

  async onModuleInit() {
    const contextId = ContextIdFactory.getByRequest(this.request);
    console.log('In onModuleInit'); // Not logged because this is [dependent upon a request-scoped service][2](?)
    this.requestService = await this.moduleRef.resolve(
      RequestService,
      contextId,
      { strict: false },
    );
  }
  async validate(type: string, { object }: ValidationArguments) {
    const contextId = ContextIdFactory.getByRequest(this.request);
    // Fails with "cannot read property resolve of undefined"
    this.requestService = await this.moduleRef.resolve(
      RequestService,
      contextId,
      { strict: false },
    );
    if (this.requestService.getIsCoffee()) {
      return true;
    }
  }
}
{/*...*}

coffee module

import {/*...*/}

@Module({
  controllers: [CoffeeController],
  imports: [RequestModule],
  providers: [IsCoffeeValidator, RequestService],
})
export class CoffeeModule {}

coffee.dto.ts

import { IsCoffee } from '../../is-coffee.decorator';

export class CoffeeDto {
  @IsCoffee()
  drink: string;
}

Coffee Controller

import {/*...*/}

@Controller('coffee')
export class CoffeeController {
  constructor(
    @Inject(RequestService) private readonly requestService: RequestService,
  ) {}

  @Post()
  checkDrink(@Body() body: CoffeeDto) {
    console.log(body);
    return this.requestService.getIsCoffee(); // should always return true, due to passing CoffeeDto validation
  }
}

Here's a contrived repo that replicates the issue. hitting POST http://localhost:3000/coffee throws the error Cannot read property 'resolve' of undefined.

How do I properly pull a request-scoped service into a custom validator?

CodePudding user response:

Because you inject @Inject(REQUEST) you still make your validator constraint as REQUEST scoped which is not possible.

How do I properly pull a request-scoped service into a custom validator?

Short answer: you don't. I'd create a custom pipe for something like that, so that it can be request scoped, rather than forcing class-valdiator to fit your needs. class-validator is great for simple constraints and schema validations, but if you need full business logic it's usually better to go with a custom pipe

  • Related