Validate nested objects using class validator and nestjs
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';export function IsNonPrimitiveArray(validationOptions?: ValidationOptions) { return (object: any, propertyName: string) => { registerDecorator({ name: 'IsNonPrimitiveArray', target: object.constructor, propertyName, constraints: [], options: validationOptions, validator: { validate(value: any, args: ValidationArguments) { return Array.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[];
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';class MultiLanguageDTO { @IsString() @IsNotEmpty() @MinLength(4) @MaxLength(40) en: string; @IsString() @IsNotEmpty() @MinLength(4) @MaxLength(40) ar: string;}export class VideoDTO { @IsDefined() @IsNotEmptyObject() @IsObject() @ValidateNested() @Type(() => MultiLanguageDTO) name!: MultiLanguageDTO;}