6

How to do validation with database like unique filed validation in nest JS using the class validator?

I created a custom validation class and tried it. But userService was undefined when I console log that service in the validate function.

import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments } from 'class-validator';
import { Injectable } from '@nestjs/common';
import { UserService } from './user.service';

@ValidatorConstraint({ name: 'unique', async: true })
@Injectable()
export class UniqueFieldValidator implements ValidatorConstraintInterface {
  constructor(private readonly userService: UserService) {
  }

  validate = async (value: any, args: ValidationArguments): Promise<boolean> => {
    console.log(this.userService); // <-- This log prints undefined
    const [entityClass, fieldName] = args.constraints;
    const entity = await this.userService.findByFieldValue({ [fieldName]: value });
    return !entity;
  }

  defaultMessage(args: ValidationArguments) {
    const [entityClass, fieldName] = args.constraints;
    return `${fieldName} must be unique`;
  }
}

I have called this class in side createUserDto Class as below.

import { IsEnum, IsNotEmpty, MinLength, Validate } from "class-validator";
import { CompanyStatuses } from "src/company/dto/company.enums";
import { User } from "../entities/user.entity";
import { UniqueFieldValidator } from "../unique-field-validator";

export class CreateUserDto {

    @IsNotEmpty()
    @MinLength(3)
    @Validate(UniqueFieldValidator, [User, 'username'])
    username: string
}

UniqueFieldValidator class is calling successfully. But because that userServices is undefined in the validate function when I sent the request to create a user, it's returning the error message saying

    TypeError: Cannot read properties of undefined (reading 'findByFieldValue')
    at UniqueFieldValidator.validate

If anyone has experience this in NestJs with class-validator please help me. Thank you!

P.S.

Below code shows how i have written my userModule and i have added both UserService and Validator class to the Providers array.also have placed @injectable() decorator top of the UserService class. but it's not working still.

import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './entities/user.entity';
import { UniqueFieldValidator } from './unique-field-validator';

@Module({
  controllers: [UserController],
  providers: [UserService,UniqueFieldValidator],
  imports: [TypeOrmModule.forFeature([User])],
  exports: [UserService,TypeOrmModule]
})
export class UserModule { }

3 Answers 3

5

First please ignore above answers. you need to create isUnique class:

import { Injectable } from '@nestjs/common'
import { ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator'
import { EntityManager } from 'typeorm'

@ValidatorConstraint({ name: 'IsUniqueConstraint', async: true })
@Injectable()
export class IsUnique implements ValidatorConstraintInterface {
  constructor(private readonly entityManager: EntityManager) {}
  async validate(value: any, args?: ValidationArguments): Promise<boolean> {
    const [tableName, column] = args?.constraints as string[]

    const dataExist = await this.entityManager
      .getRepository(tableName)
      .createQueryBuilder(tableName)
      .where({ [column]: value })
      .getExists()

    return !dataExist
  }

  defaultMessage(validationArguments: ValidationArguments): string {
    const field = validationArguments.property

    return `${field} is already exist.`
  }
}

Then you should go to AppModule and add IsUnique as provider

@Module({
  imports: [ConfigModule.forRoot(), TypeOrmModule.forRoot(DATABASE_CONFIG), UsersModule],
  providers: [ IsUnique],
})
export class AppModule {}

And Finally how to use it:

export class CreateUserDto {
  @IsAlphanumeric()
  @MinLength(3, { message: 'Username must have atleast 3 characters.' })
  @Validate(IsUnique, ['users', 'username'])
  username!: string

Final configuration will be this inside main.ts in bootstrap method

  useContainer(app.select(AppModule), { fallbackOnErrors: true })
Sign up to request clarification or add additional context in comments.

1 Comment

Please let me know if any part of this answer is not clear
2

Yeah! I found a solution and posting it to my own question for help to someone getting this issue in the future. Just add useContainer(app.select(AppModule), { fallbackOnErrors: true }); in your root method.

Then my main.ts was like this.

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { useContainer } from 'class-validator';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  useContainer(app.select(AppModule), { fallbackOnErrors: true });
  await app.listen(3000);
}
bootstrap();

I'm posting this to my own question to help someone in the future.

1 Comment

facing the same issue. I added useContainer(app.select(AppModule), { fallbackOnErrors: true }) right there in main.ts but still getting the same error, any idea how to fix ?
-1

I am using Prisma and I got the same problem, to resolve it I changed my main.ts to:

import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { ValidationPipe } from "@nestjs/common";
import { useContainer } from "class-validator";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  useContainer(app.select(AppModule), { fallbackOnErrors: true });
  await app.listen(3000);
}
bootstrap();

This is my appModule:

import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { UserModule } from "./modules/user/user.module";
import { AuthService } from "./modules/auth/auth.service";
import { AuthController } from "./modules/auth/auth.controller";
import { JwtModule } from "@nestjs/jwt";
import { jwtConstants } from "./modules/auth/constants";
import { PrismaService } from "./database/prisma/prisma.service";
import { DatabaseModule } from "./database/database.module";
import { ValidatorsModule } from "./validators/validators.module";

@Module({
  imports: [
    ConfigModule.forRoot(),
    UserModule,
    JwtModule.register({
      secret: jwtConstants.secret,
      signOptions: { expiresIn: "3600s" },
    }),
    DatabaseModule,
    ValidatorsModule,
  ],
  controllers: [AuthController],
  providers: [AuthService, PrismaService],
  exports: [PrismaService],
})
export class AppModule {}

And my databaseModule:

import { Module } from "@nestjs/common";
import { PrismaService } from "./prisma/prisma.service";

@Module({
  providers: [PrismaService],
  exports: [PrismaService],
})
export class DatabaseModule {}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.