[ ] Regression
[ ] Bug report
[ ] Feature request
[X] Documentation issue or request
[X] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
This fails because regionId is a string when called.
import { IsInt} from 'class-validator';
export class GetLeaderboardDto {
@IsInt()
public readonly regionId: number;
}
Having a possibility to parse it as number inbetween, like this:
import { IsInt, IsNumberString } from 'class-validator';
export class GetLeaderboardDto {
@IsNumberString()
@ParseNumber()
@IsInt()
@Min(0)
@Max(100)
public readonly regionId: number;
}
I am using a DTO class for each of my endpoints (e. g. GET requests with params) where I want to validate the input as strictly as possible.
Currently I can not use the more advanced number validitions (such as Min()
IsInt()
etc.) as my DTO always gets a string. I am aware I can use IsNumberString()
but due to the fact that the DTO always has a string and doesn't get the parsed number.
Does my "feature request" make sense or are there any other workarounds which are appropriate for my use case?
Hey @weeco this issue is not directly related to NestJS so I guess you get better answers on the class-validator repository.
I think to some extent it is, because class-validator hasn't been strictly created for validations of web input. They would probably just say, just parse it as number before validating it.
Since class-validator is the recommended library for validating the input I wonder if NestJS helps me with this issue, where I would for instance try to parse it as number before executing the validation pipeline?
BTW: This issue in class-validator also addresses this issue, and it looks like it's not part of the library by design: https://github.com/typestack/class-validator/issues/284
Hello @weeco, you have to use the class-transformer package to transform the body of the request to the desired type.
You can use the provided ValidationPipe.
Add this code in your main.ts file if you want to apply it to all the app.
app.useGlobalPipes(new ValidationPipe({
transform: true,
}));
That doesn't work because it will never reach the point where it could transform the value, as it's immediately rejected by the validationpipeline.
Are you using the ValidationPipe ?
In the source code : https://github.com/nestjs/nest/blob/c6ec8878e0864469fa5e83d97d3ba07df397b09e/packages/common/pipes/validation.pipe.ts#L40
The transformation is clearly done before the validation.
Just tried it again:
When I change IsNumberString()
to IsNumber
decorator, the returned error is:
{
"statusCode": 400,
"error": "Bad Request",
"message": [
{
"target": {
"regionId": "0"
},
"value": "0",
"property": "regionId",
"children": [],
"constraints": {
"isNumber": "regionId must be a number"
}
}
]
}
I am using a global pipe:
app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true, disableErrorMessages: false }));
DTO looks like this:
export class GetBandLeaderboardDto {
@ApiModelProperty({
description: "Band Leaderboard's region id",
required: true,
type: 'number'
})
@IsNumber()
public readonly regionId: number;
}
Try to remove the IsNumber() from your property DTO. class-validator should be able to infer it from the type.
As @abouroubi has shown, ValidationPipe
is using plainToClass
function before performing an actual validation, thus, you should be able to use @Transform()
decorator from class-transformer package
@abouroubi The purpose is to use the numeric validation options (e. g. IsInt
, Min
etc.) which all fail since the given value is a string: "1"
. Anyways I tried it and hoped that it would THEN try to parse the string as number. However that didn't work either:
{
"statusCode": 400,
"error": "Bad Request",
"message": [
{
"target": {
"regionId": "1"
},
"value": "1",
"property": "regionId",
"children": [],
"constraints": {
"isInt": "regionId must be an integer number"
}
}
]
}
@kamilmysliwiec As far as I know the @Transform() decorator is only necessary for custom transformers no? So if it's supposed to work as shown in my code above it seems like there is a bug. Should I then create an own minimal repository for it, so that you can replicate the issue on your PC?
@weeco
https://github.com/typestack/class-transformer#%D1%81onverting-date-strings-into-date-objects
Same technique can be used with Number, String, Boolean primitive types when you want to convert your values into these types.
Are you sure that class-validator
is going to automatically cast these types?
@kamilmysliwiec You were right. I have to explicitly cast the Number type on my own. I thought it would work on primitives by default. Intereseting observation however:
@IsNumberString()
@Type(() => Number)
@IsInt()
public readonly regionId: number;
When I now pass 0
as parameter for the regionId, it would fail because it first tries to cast the 0
to a number, which does succeed. Then it runs the IsNumberString()
validation which fails because now it as number and not a string anymore.
I wished it would run these validation checks in the order I put them in there, but it's not a big deal. The type cast doesn't fail for non numbers, so that I will use it rather as "type assertion":
@Type(() => Number)
@IsInt()
public readonly regionId: number;
This one will fail with input "abc" because it's not an int.
I did this in case anyone is looking
export class User {
@Transform(parseInt)
@IsInt()
age: number;
}
I think this should be in the documentation, it helped me a lot. :dancer:
this is my little example :D
import { IsNumber, IsNumberString, IsString } from 'class-validator';
import { Type } from 'class-transformer';
export class QueryUsersDTO {
@Type(() => String)
@IsString()
readonly companyID: string;
@Type(() => Number)
@IsNumber()
readonly limit: number;
@Type(() => Number)
@IsNumber()
readonly currentPage: number;
}
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Most helpful comment
I think this should be in the documentation, it helped me a lot. :dancer:
this is my little example :D