Home > OS >  NestJS Modify Request Body, and then have the ValidationPipe evaluate the contents
NestJS Modify Request Body, and then have the ValidationPipe evaluate the contents

Time:09-28

I have my API class that has URL parameters (/user/:id) and then there are rules on the User Id in my DTO.

When a request is made to update information about my route, the DTO is what is saved. I have the Global validation running which will evaluate the request Body, and the ParseIntPipe() running on the parameter.

However, in some routes, I have multiple parameters in the URL. What I want to be able to do is modify the Request Body with the parameters from the route, to ensure they are the same.

For instance, lets take the following route:

@Post('/date/:start/:end')
public async GetRecords(
    @Param('start', ParseIntPipe) Start: number,
    @Param('end', ParseIntPipe) End: number,
    @Body() RequestData: GetDTO
): Promise<Array<ResponseDTO>> {
    return service.GetAllRecordsInRange(Start, End, RequestData);
}

The standard validation works well in the above case. However, in some routes, the parameters are included in the request data as the payload may have these fields, and not the URL directly. For instance, the UserId might be in the payload as an additional filter, but could also be in a different route.

In this case, typically our code updates the RequestData with the values from the route, and then calls the service.

@Post('/date/:start/:end')
public async GetRecords(
    @Param('start', ParseIntPipe) Start: number,
    @Param('end', ParseIntPipe) End: number,
    @Body() RequestData: GetDTO
): Promise<Array<ResponseDTO>> {
    RequestData.StartValue = Start; // Use the URL value as the start, ignoring the value in the request body
    RequestData.EndValue = End; // Use the URL value as the end, ignoring the value in the request body. 

    return service.GetAllRecordsInRange(Start, End, RequestData);
}

In this example, after I modify the RequestData, I would like the ValidationPipe to validate that the values for Start/End are included in the payload check. If these are required fields, and not provided with the original body request, the error returns they are missing, even though the controller code is updating these values.

Secondly, I would like to validate that the End is larger than the Start, etc., which has those validations in the DTO. Is there any way to get the ValidationPipe to run after my changing the request data, without having to manually call the validation when I have changed the payload?

CodePudding user response:

Rather than updating the values in the controller, you could use an interceptor to update them because interceptors run before pipes. Though, if you do that, then you'll need to use @Type(() => Number) to ensure that class-transformer takes the string and turns it into a number. Then you could use class-validator decorators as a part of you GetDTO and use the ValidationPipe to do all transformation and validation for you. This would be a pretty specific interceptor, so it would most likely only be bound to this route, but it could possibly be written in a more generic way to make it work for any route necessary


Example interceptor:

@Injectable()
export class StartEndParamToBodyInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const req = context.switchToHttp().getRequest();
    const { params, body } = req; // get the params and body from req
    body = { ...body, ...params }; // moves the params to the body
    req.body = body; // just to be safe. I believe you could omit this, but rather safe than sorry
    return next.handle(); // keep the request going
  }
}
  • Related