Reputation: 185
Let's say I have following route:
companies/{companyId}/departments/{departmentId}/employees
Is it possible to validate both resources ids (companyId
, departmentId
) separately? I've tried following but it's not working.
class ResourceId {
@IsNumberString()
@StringNumberRange(...) // my custom validator
id: number;
}
@Get(':companyId/departments/:departmentId/employees')
getEmployees(
@Param('companyId') companyId: ResourceId,
@Param('departmentId') departmentId: ResourceId,
) {}
I have multiple cases when there is more than one parameter in the single route. I would not like to create separate validation class for every route. Is there a way to handle this problem in a different way?
Upvotes: 6
Views: 8002
Reputation: 707
As of 2022, NestJS docs say that it's possible to validate route params using the built-in validation pipe.
In a controller:
@Get(':id')
findOne(@Param() params: FindOneParams) {
return 'This action returns a user';
}
Validation class:
import { IsNumberString } from 'class-validator';
export class FindOneParams {
@IsNumberString()
id: number;
}
Ref: https://docs.nestjs.com/techniques/validation#auto-validation
Upvotes: 13
Reputation: 60377
The problem is that you have a plain string here. For the validation with class-validator
to work, you must instantiate a class, in your case ResourceId
. The built-in ValidationPipe
expects the value to be {id: '123'}
instead '123'
to be able to transform it automatically. But you can easily create your own validation pipe, that does this extra transformation.
export class ParamValidationPipe implements PipeTransform {
async transform(value, metadata: ArgumentMetadata) {
if (metadata.type === 'param') {
// This is the relevant part: value -> { id: value }
const valueInstance = plainToClass(metadata.metatype, { id: value });
const validationErrors = await validate(valueInstance);
if (validationErrors.length > 0) {
throw new BadRequestException(validationErrors, 'Invalid route param');
}
return valueInstance;
} else {
return value;
}
}
}
You can then use it on your controller:
@UsePipes(ParamValidationPipe)
@Get(':companyId/departments/:departmentId/employees')
getEmployees(
@Param('companyId') companyId: ResourceId,
@Param('departmentId') departmentId: ResourceId,
) {
return `id1: ${companyId.id}, id2: ${departmentId.id}`;
}
Upvotes: 4