-
Notifications
You must be signed in to change notification settings - Fork 837
Description
When using class-transformer with class-validator in NestJS, and applying stopAtFirstError: true, the validation order can be unintuitive. Specifically, if a property is transformed into NaN (e.g., due to an undefined or missing value), the @min or @max decorators may throw an error before the @ISINT decorator, even though @ISINT is listed first.
Steps to Reproduce:
Define a DTO with @type(), @ISINT(), and @min() decorators:
import { Type } from 'class-transformer';
import { IsInt, Min } from 'class-validator';
export class PaginationDto {
@type(() => Number)
@ISINT({ message: 'Page must be an integer' })
@min(1, { message: 'Page must be at least 1' })
readonly page: number;
}
Send a request with an undefined or missing page property.
Apply stopAtFirstError: true in the ValidationPipe:
app.useGlobalPipes(
new ValidationPipe({
transform: true,
stopAtFirstError: true,
}),
);
Expected Behavior:
The validation should stop at the first error encountered. In this case, the first error should be from the @ISINT decorator, indicating that the value is not an integer.
Actual Behavior:
The validation stops at the @min decorator, which throws an error because NaN is not a valid number, overriding the @ISINT error.
Cause:
The @min and @max decorators internally call isNumber() from validator.js, which detects NaN and throws an error before the @ISINT decorator has a chance to validate.
Suggested Fix:
Ensure that the decorators respect the order in which they are applied and that stopAtFirstError functions as intended, stopping at the first decorator that fails validation.