yuva.le
yuva.le

Reputation: 339

NestJS: How to transform an array in a @Query object

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

Answers (7)

Bass
Bass

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

Lorenzo Regalado
Lorenzo Regalado

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

fabiangamboa95
fabiangamboa95

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

maveriq
maveriq

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

chrismarx
chrismarx

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

Juan Diego Garcia
Juan Diego Garcia

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[]> {
                // ...
            }
  • "class-transformer": "^0.4.0"
  • "class-validator": "^0.13.1"

Upvotes: 12

Jay McDoniel
Jay McDoniel

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-validatorand 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

Related Questions