Reputation: 339
I'm new to NestJS and I am trying to fill a filter DTO from query Parameters.
Here is what I have:
Query:
localhost:3000/api/checklists?stations=114630,114666,114667,114668
Controller
@Get()
public async getChecklists(@Query(ValidationPipe) filter: ChecklistFilter): Promise<ChecklistDto[]> {
// ...
}
DTO
export class ChecklistFilter {
@IsOptional()
@IsArray()
@IsString({ each: true })
@Type(() => String)
@Transform((value: string) => value.split(','))
stations?: string[];
// ...
}
With this, the class validator does not complain, however, in the filter object stations is not actually an array but still a single string.
I want to transform it into an array within the validation pipe. How can I achieve that?
Upvotes: 27
Views: 57687
Reputation: 61
Todays, with the version of class transformer
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
I did it like this, for check UUIDSv4 like this: ade3f65f-ee35-4224-ab85-f1c8368a375a,4aab51f8-1f4d-4d33-924f-0976bc902b01
@IsOptional()
@IsArray()
@IsUUID('4', { each: true })
@Type(() => String)
@Transform((params) => params.value.split(','))
readonly ids?: string[];
Dont forget to add this to main.ts
app.useGlobalPipes(
new ValidationPipe({
transform: true,
}),
Upvotes: 0
Reputation: 233
Had a similar issue, what works for me is apply a custom transform:
export class ChecklistFilter {
@ApiProperty({ type: [Number] })
@IsOptional()
@IsArray()
@Transform((item) => item.value.map((v) => parseInt(v, 10)))
stations?: number[];
//...
}
Upvotes: 0
Reputation: 236
the only problem you had there is in the order of validations. You can do it like this:
export class ChecklistFilter {
@IsOptional()
@Transform((params) => params.value.split(',').map(Number))
@IsInt({ each: true })
stations?: number[]
// ...
}
If you want numbers instead of ints: @IsNumber({}, { each: true })
Upvotes: 4
Reputation: 516
You can change your initial query a bit:
localhost:3000/api/checklists?stations[]=114630&stations[]=114666&stations[]=114667&stations[]=114668
And your controller:
@Get()
public async getChecklists(@Query('stations') filter: string[]): Promise<ChecklistDto[]> {
// ...
}
This way the default mechanism will work fine and will convert query params into string array and no any additional dependencies or handling required.
You also can wrap it with your DTO if needed, but you get the idea.
Upvotes: 6
Reputation: 12555
This can be handled without a separate DTO class using the ParseArrayPipe
:
@Get()
findByIds(
@Query('ids', new ParseArrayPipe({ items: Number, separator: ',' }))
ids: number[],
) {
console.log(ids);
console.log(Array.isArray(ids)); //returns true
return 'This action returns users by ids';
}
ref: https://docs.nestjs.com/techniques/validation#parsing-and-validating-arrays
Upvotes: 21
Reputation: 201
export class ChecklistFilter {
@IsOptional()
@IsArray()
@IsString({ each: true })
@Type(() => String)
@Transform(({ value }) => value.split(','))
stations?: string[];
// ...
}
--
@Get()
public async getChecklists(@Query() filter: ChecklistFilter): Promise<ChecklistDto[]> {
// ...
}
Upvotes: 12
Reputation: 70191
You can pass an instance of the ValidationPipe
instead of the class, and in doing so you can pass in options such as transform: true
which will make class-validator
and class-transformer
run, which should pass back the transformed value.
@Get()
public async getChecklists(@Query(new ValidationPipe({ transform: true })) filter: ChecklistFilter): Promise<ChecklistDto[]> {
// ...
}
Upvotes: 14