Skip to content Skip to sidebar Skip to footer

Validate Nested Objects Using Class Validator And Nestjs

I'm trying to validate nested objects using class-validator and NestJS. I've already tried following this thread by using the @Type decorator from class-transform and didn't have a

Solution 1:

You are expecting positions: [1] to throw a 400 but instead it is accepted.

According to this Github issue, this seems to be a bug in class-validator. If you pass in a primitive type (boolean, string, number,...) or an array instead of an object, it will accept the input as valid although it shouldn't.


I don't see any standard workaround besides creating a custom validation decorator:

import { registerDecorator, ValidationOptions, ValidationArguments } from'class-validator';

exportfunctionIsNonPrimitiveArray(validationOptions?: ValidationOptions) {
  return(object: any, propertyName: string) => {
    registerDecorator({
      name: 'IsNonPrimitiveArray',
      target: object.constructor,
      propertyName,
      constraints: [],
      options: validationOptions,
      validator: {
        validate(value: any, args: ValidationArguments) {
          returnArray.isArray(value) && value.reduce((a, b) => a && typeof b === 'object' && !Array.isArray(b), true);
        },
      },
    });
  };
}

and then use it in your dto class:

@ValidateNested({ each: true })
@IsNonPrimitiveArray()
@Type(() => PositionDto)
positions: PositionDto[];

Solution 2:

for me, I would able to validate nested object with 'class-transformer'

import { Type } from'class-transformer';

full example:

import {
  MinLength,
  MaxLength,
  IsNotEmpty,
  ValidateNested,
  IsDefined,
  IsNotEmptyObject,
  IsObject,
  IsString,
} from 'class-validator';
import { Type } from 'class-transformer';

classMultiLanguageDTO {
  @IsString()@IsNotEmpty()@MinLength(4)@MaxLength(40)
  en: string;

  @IsString()@IsNotEmpty()@MinLength(4)@MaxLength(40)
  ar: string;
}

export classVideoDTO {
  @IsDefined()@IsNotEmptyObject()@IsObject()@ValidateNested()@Type(() => MultiLanguageDTO)
  name!: MultiLanguageDTO;
}

Solution 3:

I faced the same issue, so I created my own ValidateNested decorator.

import {
  ValidationOptions,
  registerDecorator,
  ValidationArguments,
  validateSync,
} from'class-validator';
import { plainToClass } from'class-transformer';

/**
 * @decorator
 * @description A custom decorator to validate a validation-schema within a validation schema upload N levels
 * @param schema The validation Class
 */exportfunctionValidateNested(
  schema: new () => any,
  validationOptions?: ValidationOptions
) {
  returnfunction (object: Object, propertyName: string) {
    registerDecorator({
      name: 'ValidateNested',
      target: object.constructor,
      propertyName: propertyName,
      constraints: [],
      options: validationOptions,
      validator: {
        validate(value: any, args: ValidationArguments) {
          args.value;
          if (Array.isArray(value)) {
            for (let i = 0; i < (<Array<any>>value).length; i++) {
              if (validateSync(plainToClass(schema, value[i])).length) {
                returnfalse;
              }
            }
            returntrue;
          } elsereturnvalidateSync(plainToClass(schema, value)).length
              ? false
              : true;
        },
        defaultMessage(args) {
          if (Array.isArray(args.value)) {
            for (let i = 0; i < (<Array<any>>args.value).length; i++) {
              return (
                `${args.property}::index${i} -> ` +
                validateSync(plainToClass(schema, args.value[i]))
                  .map((e) => e.constraints)
                  .reduce((acc, next) => acc.concat(Object.values(next)), [])
              ).toString();
            }
          } elsereturn (
              `${args.property}: ` +
              validateSync(plainToClass(schema, args.value))
                .map((e) => e.constraints)
                .reduce((acc, next) => acc.concat(Object.values(next)), [])
            ).toString();
        },
      },
    });
  };
}

Then you can use it like -

classSchema2 {

  @IsNotEmpty()
  @IsString()
  prop1: string;

  @IsNotEmpty()
  @IsString()
  prop2: string;
}


classSchema1 {
  @IsNotEmpty()
  @IsString()
  prop3: string;

  @ValidateNested(Schema2)
  nested_prop: Schema2;
}

Works for both non-primitive arrays and javascript objects.

Post a Comment for "Validate Nested Objects Using Class Validator And Nestjs"