Reputation: 357
I build nestjs with a typescript project and I'm trying to validate filters query params. I want only filter keys and values can pass validation. example for good params: pass validation
any-endpoint?filters={"userIds"=["1","2","5"],"ages"=[25]}
any-endpoint?filters={"names"=["david","samuel"],"ages"=[21]}
example for bad params: failed validation
any-endpoint?filters={"blaBla"=["1","2","5"],"ages"=[25]} // here the key blaBla not one of the filters options
any-endpoint?filters={"names"=[1,2]} // here the values of names is not string, it should be string
my code
import { IsNumber, IsOptional,IsArray, IsString } from 'class-validator';
class FiltersOptionsDto {
@IsOptional()
@IsArray()
@IsString({ each: true })
userIds?: string[];
@IsOptional()
@IsArray()
@IsString({ each: true })
ages?: number[];
@IsOptional()
@IsArray()
@IsNumber({}, { each: true })
names?: string[];
}
export class AQueryDto {
@IsOptional()
// what to add here?
filters?: FiltersOptionsDto;
}
how to do it?
Upvotes: 1
Views: 2509
Reputation: 70432
All right, so if you really want to send a query in that format, you can use the @Transform()
decorator from class-transformer
and use plainToClass
to transform the filters value. To do this though, you need a couple of things.
The @ValidateNested()
decorator is necessary to make sure that it can be validated as expected. Namely so the child object can be validated.
The @Transform()
decorator will need to be used. This is working for me
import { IsOptional, ValidateNested } from 'class-validator';
import { Transform, plainToClass } from 'class-transformer'
import { FiltersOptionsDto } from './filter.dto';
export class AQueryDto {
@IsOptional()
@ValidateNested()
@Transform((value) => plainToClass(FiltersOptionsDto, JSON.parse(value.replace(/=/g, ':'))))
filters?: FiltersOptionsDto;
}
Each =
needs to be made to a :
so that it is the correct JSON format (I also separated out the classes as I prefer one class per file). With the above, the value passes in to filters
will be taken from a string value to a JSON (plain), and then from a plain to a class.
forbidNonWhitelisted: true
in your ValidationPipe
. I've done this like so:@Module({
providers: [
{
provide: APP_PIPE,
useValue: new ValidationPipe({ forbidNonWhitelisted: true })
}
]
})
export class AppModule {}
(Along with the other providers and controllers of course). I also like to add transform: true
so that it's no longer JSON or string, but an actual class.
Now you can pass in
?filters={"userIds"=["1","2","5"],"ages"=["25"]}
And it will succeed while trying to pass in
?filters={"blaBla"=["1","2","5"],"ages"=[25]}
It will fail.
Upvotes: 3