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"