22

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?

yuva.le
  • 289
  • 1
  • 3
  • 9

6 Answers6

13

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-validatorand class-transformer run, which should pass back the transformed value.

@Get()
public async getChecklists(@Query(new ValidationPipe({ transform: true })) filter: ChecklistFilter): Promise<ChecklistDto[]> {
    // ...
}
Jay McDoniel
  • 57,339
  • 7
  • 135
  • 147
11
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[]> {
                // ...
            }
  • "class-transformer": "^0.4.0"
  • "class-validator": "^0.13.1"
  • Hi, any idea on how to achieve this for an array of numbers in a query param? imagine im trying to validate [www.url.com/path?ids=1,2,3] , where [ids] should be an array of numbers, and nothing else. tried converting your answer, but with no success so far. – Maor Barazani Nov 07 '21 at 22:12
  • 6
    `@IsArray() @IsInt({ each: true }) @Transform(({ value }) => value.trim().split(',').map(id=>Number(id))) @ApiProperty({ type: [Number], format: 'form' }) ids?: number[];` **/path?ids=1,2,3,4** – Juan Diego Garcia Nov 10 '21 at 20:42
10

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

chrismarx
  • 11,488
  • 9
  • 84
  • 97
  • 1
    this was almost exactly what i needed, thank you! only exception is i needed array of strings instead of numbers but that was easy enough to figure out :) – Qamar Stationwala Aug 28 '23 at 22:07
5

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.

maveriq
  • 456
  • 4
  • 14
2

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 })

fabiangamboa95
  • 216
  • 2
  • 9
0

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[];
  //...
}