I have a query parameter in my REST API, which values should be restricted by an enum type. I'm looking for a way to throw a "Bad Request" error, when a client gives something different.
My enum looks like this:
export enum Precision {
S = 's',
MS = 'ms',
U = 'u',
NS = 'ns',
}
My controller function looks like this:
@Get(':deviceId/:datapoint/last')
@ApiOkResponse()
@ApiQuery({name: 'precision', enum: Precision})
getLastMeasurement(
@Param('deviceId') deviceId: string,
@Param('datapoint') datapoint: string,
@Query('precision') precision: Precision = Precision.S,
@Res() response: Response,
) {
console.log(precision);
....
response.status(HttpStatus.OK).send(body);
}
My problem here is that the function accepts other values, too (for example I can send an f as the query parameter's value). The Function won't return an error to the client, but I want to without writing an if else block at the beginning of each controller function.
I guess there is a rather simple solution to this, but when I try to look it up on the internet I always get results for class validation in DTOs, but not for a simple enum validation directly in the query param/REST controller.
Thanks for your time,
J
There is 2 issues here.
The first one is that you are passing the default value of precision
params in the wrong way. You must use the DefaultValuePipe
like this:
getLastMeasurement(
... // some other params
@Query('precision', new DefaultValuePipe(Precision.S)) precision: Precision
) {
... // do something
}
The second one is the enum validation. NestJS comes with only 6 types of validationPipes and none of them validates an enum, so you must create your own custom validation pipe to validate enums.
There is 2 possible ways that you can do that:
Based on this https://docs.nestjs.com/pipes#custom-pipes, it would be something like:
import { BadRequestException, PipeTransform } from '@nestjs/common';
import { isDefined, isEnum } from 'class-validator';
export class PrecisionValidationPipe implements PipeTransform<string, Promise<Precision>> {
transform(value: string): Promise<Precision> {
if (isDefined(value) && isEnum(value, Precision)) {
return Precision[value];
} else {
const errorMessage = `the value ${value} is not valid. See the acceptable values: ${Object.keys(
Precision
).map(key => Precision[key])}`;
throw new BadRequestException(errorMessage);
}
}
}
and then in your request, it would be like
getLastMeasurement(
@Query('precision', PrecisionValidationPipe, new DefaultValuePipe(Precision.S)) precision: Precision,
) {
console.log(precision);
....
response.status(HttpStatus.OK).send(body);
}
import { BadRequestException, Injectable, PipeTransform } from '@nestjs/common';
import { isDefined, isEnum } from 'class-validator';
@Injectable()
export class EnumValidationPipe implements PipeTransform<string, Promise<any>> {
constructor(private enumEntity: any) {}
transform(value: string): Promise<any> {
if (isDefined(value) && isEnum(value, this.enumEntity)) {
return this.enumEntity[value];
} else {
const errorMessage = `the value ${value} is not valid. See the acceptable values: ${Object.keys(this.enumEntity).map(key => this.enumEntity[key])}`;
throw new BadRequestException(errorMessage);
}
}
}
and then in your request, it would be like
getLastMeasurement(
@Query('precision', new EnumValidationPipe(Precision), new DefaultValuePipe(Precision.S)) precision: Precision,
) {
console.log(precision);
....
response.status(HttpStatus.OK).send(body);
}