How to add unique field validation in nest js with class-validator
Asked Answered
S

3

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 { }
Stratify answered 7/3, 2023 at 9:51 Comment(0)
S
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.

Stratify answered 7/3, 2023 at 13:42 Comment(1)
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 ?Brunn
T
2

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 })
Trichosis answered 27/3, 2024 at 13:43 Comment(1)
Please let me know if any part of this answer is not clearTrichosis
M
0

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 {}
Miyamoto answered 10/7, 2024 at 1:30 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.