I'm trying to create custom validator for my input class in NestJS. This is what I have done:
// is-building-id-provided.validator.ts
import { Injectable } from "@nestjs/common";
import { AddToQueueInput } from "@warp-core/building-queue/input/add-to-queue.input";
import { BuildingZoneService } from "@warp-core/building-zone/building-zone.service";
import { registerDecorator, ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface } from "class-validator";
@ValidatorConstraint({ async: true })
@Injectable()
export class IsBuildingIdProvidedConstraint implements ValidatorConstraintInterface {
constructor(
protected readonly buildingZoneService: BuildingZoneService
) {}
async validate(buildingId: number, args: ValidationArguments) {
if (buildingId) {
return true;
}
const addToQueue = args.object as AddToQueueInput;
const buildingZone = await this.buildingZoneService.getSingleBuildingZone(
addToQueue.counterPerHabitat
);
if (!buildingZone.buildingId) {
return false;
}
return true;
}
defaultMessage(args: ValidationArguments) {
return `Building Id is required to create new building on existing building zone`;
}
}
export function IsBuildingIdProvided(validationOptions?: ValidationOptions) {
return function (object: Object, propertyName: string) {
registerDecorator({
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
constraints: [],
validator: IsBuildingIdProvidedConstraint,
});
};
}
// add-to-queue.input.ts
import { Field, InputType, Int, PartialType } from "@nestjs/graphql";
import { IsBuildingIdProvided } from "@warp-core/building-queue/input/validator/is-building-id-provided.validator";
import { BuildingQueueElementModel } from "@warp-core/database/model/building-queue-element.model";
@InputType({description: "Creates new element in queue"})
export class AddToQueueInput extends PartialType(BuildingQueueElementModel)
{
@Field(type => Int, {
description: "Id of building type that will be constructed. If building is already placed, that field will be ignored"
})
@IsBuildingIdProvided()
buildingId?: number;
@Field(type => Int, {description: "How much levels will be build"})
endLevel: number;
}
// building-queue.module.ts
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { AuthModule } from "@warp-core/auth/auth.module";
import { BuildingQueueAddService } from "@warp-core/building-queue/building-queue-add.service";
import { BuildingQueueResolver } from "@warp-core/building-queue/building-queue.resolver";
import { IsBuildingIdProvidedConstraint } from "@warp-core/building-queue/input/validator/is-building-id-provided.validator";
import { BuildingZoneModule } from "@warp-core/building-zone/building-zone.module";
import { BuildingModule } from "@warp-core/building/building.module";
import { DatabaseModule } from "@warp-core/database/database.module";
@Module({
providers: [
BuildingQueueAddService,
BuildingQueueResolver,
IsBuildingIdProvidedConstraint,
],
imports: [
BuildingZoneModule,
BuildingModule,
DatabaseModule,
ConfigModule,
AuthModule,
],
exports: [
]
})
export class BuildingQueueModule {
}
// main.ts
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from '@warp-core/app.module';
import { useContainer } from 'class-validator';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
transform: true,
}),
);
useContainer(app.select(AppModule), { fallbackOnErrors: true });
await app.listen(3000);
console.log("App is loaded at " + (await app.getUrl()) + "/graphql URL");
}
bootstrap();
As you can see, my Constraint class have Injectable
decorator, also all external modules are imported in module
file and in main.ts
file I call useContainer
function from class-validator
package. I think I have done everything mentioned in other stack questions and in NestJS github issue. Unfortunately every time when I'm trying to use injected functions, buildingZoneService
is undefined. I have no idea what to do more with that issue, I'm out of ideas...
One of validator dependencies has a Request
scope, and that is impossible to be injected into class-validator
custom class. (https://github.com/nestjs/nest/issues/5566)
Instead of that I have decided to use Validation pipe, as described in Nest.js documentation: https://docs.nestjs.com/pipes#binding-validation-pipes