'How to inject service to validator constraint interface in nestjs using class-validator?
I'm trying to inject my users service into my validator constraint interface but it doesn't seem to work:
import { ValidatorConstraintInterface, ValidatorConstraint, ValidationArguments, registerDecorator, ValidationOptions } from "class-validator";
import { UsersService } from './users.service';
@ValidatorConstraint({ async: true })
export class IsEmailAlreadyInUseConstraint implements ValidatorConstraintInterface {
constructor(private usersService: UsersService) {
console.log(this.usersService);
}
validate(email: any, args: ValidationArguments) {
return this.usersService.findUserByEmail(email).then(user => {
if (user) return false;
return true;
});
return false;
}
}
But, as usersService is logged null, I can't access its methods.
Any insight on this matter?
Solution 1:[1]
For those who might be suffering from this issue:
class-validator requires you to use service containers if you want to inject dependencies into your custom validator constraint classes. From: https://github.com/typestack/class-validator#using-service-container
import {useContainer, Validator} from "class-validator";
// do this somewhere in the global application level:
useContainer(Container);
So that we need to add the user container function into the global application level.
1. Add the following code to your main.ts bootstrap function after app declaration:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
useContainer(app.select(AppModule), { fallbackOnErrors: true });
...}
The {fallbackOnErrors: true} is required, because Nest throw Exception when DI doesn't have required class.
2. Add Injectable() to your constraint:
import {ValidatorConstraint, ValidatorConstraintInterface} from 'class-validator';
import {UsersService} from './user.service';
import {Injectable} from '@nestjs/common';
@ValidatorConstraint({ name: 'isUserAlreadyExist', async: true })
@Injectable() // this is needed in order to the class be injected into the module
export class IsUserAlreadyExist implements ValidatorConstraintInterface {
constructor(protected readonly usersService: UsersService) {}
async validate(text: string) {
const user = await this.usersService.findOne({
email: text
});
return !user;
}
}
3. Inject the constraint into your module as a provider and make sure that the service you intend to inject into your constraint are also available to a module level:
import {Module} from '@nestjs/common';
import { UsersController } from './user.controller';
import { UsersService } from './user.service';
import { IsUserAlreadyExist } from './user.validator';
@Module({
controllers: [UsersController],
providers: [IsUserAlreadyExist, UsersService],
imports: [],
exports: []
})
export class UserModule {
}
Solution 2:[2]
You'll need to update class-validator
's container to use the Nest application to allow for Dependency Injection everywhere. This GitHub Issue goes through how to do it and some struggles people have faced with it.
Quick fix without reading the link:
async function bootstrap() {
const app = await NestFactory.create(ApplicationModule);
useContainer(app, { fallback: true });
await app.listen(3000);
}
bootstrap();
When doing this, make sure you also register your validators as you normally would any nest @Injectable()
Solution 3:[3]
By the way, this doesn't work in e2e test. This is the way how I get it running.
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
app.useGlobalPipes(GetValidationPipe());
useContainer(app.select(AppModule), { fallbackOnErrors: true });
await app.init();
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Dina Flies |
Solution 2 | Jay McDoniel |
Solution 3 | passport4j |