I would like to validate an array of objects sent via Postman like that:
{
pit_fee: [
{
range: [1,2]
fee:25
},
{
range: [3,4]
fee:30
},
}
This is my validation schema using class-validators:
export class PitFeeObject {
@IsNumber({}, { each: true })
range: number[];
@IsNumber()
fee: number;
}
export class CreateConfigSchema {
@IsArray()
@ValidateNested({ each: true })
pit_fee: PitFeeObject[];
}
And this is my middleware function that takes the request body as input and validates it:
const validationMiddleware = (
type: any,
value: string | 'body' | 'query' | 'params' = 'body',
skipMissingProperties = false,
whitelist = true,
forbidNonWhitelisted = true,
): RequestHandler => {
return (req, res, next) => {
validate(plainToInstance(type, req[value]), { skipMissingProperties, whitelist, forbidNonWhitelisted }).then((errors: ValidationError[]) => {
if (errors.length > 0) {
const message = errors.map((error: ValidationError) => Object.values(error.constraints)).join(', ');
next(new HttpException(400, message));
} else {
next();
}
});
};
};
export default validationMiddleware;
The problem is that the @ValidateNested()
decorator doesn't put error.constraint
like all other decorators do, but it gives error.children.children.constraint
.
So I should divide my function with an if statement. If error.constraint
it catches the constraint else I map error 2 times, find the constraint and catch it. But mapping 2 times is not a good practice as it may be 2 times or 3 or any other number that change as many nested objects I have inside my array.
I think you can recursively traverse the error object to find the constraints, regardless of the nesting level.
Example
const validationMiddleware = (
type: any,
value: string | 'body' | 'query' | 'params' = 'body',
skipMissingProperties = false,
whitelist = true,
forbidNonWhitelisted = true,
): RequestHandler => {
return (req, res, next) => {
validate(plainToInstance(type, req[value]), { skipMissingProperties, whitelist, forbidNonWhitelisted }).then((errors: ValidationError[]) => {
if (errors.length > 0) {
const errorMessages = getErrorMessages(errors);
next(new HttpException(400, errorMessages.join(', '));
} else {
next();
}
});
};
};
function getErrorMessages(errors: ValidationError[]): string[] {
const messages: string[] = [];
for (const error of errors) {
if (error.constraints) {
messages.push(...Object.values(error.constraints));
}
if (error.children && error.children.length > 0) {
messages.push(...getErrorMessages(error.children));
}
}
return messages;
}
export default validationMiddleware;