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 any luck. This what I have:
DTO:
class PositionDto {
@IsNumber()
cost: number;
@IsNumber()
quantity: number;
}
export class FreeAgentsCreateEventDto {
@IsNumber()
eventId: number;
@IsEnum(FinderGamesSkillLevel)
skillLevel: FinderGamesSkillLevel;
@ValidateNested({ each: true })
@Type(() => PositionDto)
positions: PositionDto[];
}
I'm also using built-in nestjs validation pipe, this is my bootstrap:
async function bootstrap() {
const app = await NestFactory.create(ServerModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(config.PORT);
}
bootstrap();
It's working fine for other properties, the array of objects is the only one not working.
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[];