0

Following on from This question, I have created an interceptor, and I am able to map my parameters into my DTO. However, the number parameters, are not being converted to numeric during the validation.

The DTO:

export class GetDTO {
    @IsDefined() @IsNumber() @Max(50) @Min(1) @Type( () => Number) public Start: number;
    @IsDefined() @IsString() @MaxLength(64) @MinLength(5) public Description: string;
    

Controller defines the local copy of the interceptor

@UseInterceptors(AddParametersToRequestBodyInterceptor)
export class MyController {

@Post('/:Start')
public async GetData(
    @Param('Start', ParseIntPipe) Start: number,
    @Body() Data: GetDTO
): Promise<ResponseDTO> {
}

When debugging this, I can see it works as it takes the Start parameter from the URL (POST /25), and does add it into the request body, and replaces the value with a string '25' (or adds it if it is not in the request body).

import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class AddParametersToRequestBodyInterceptor 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
        const NewRequestBody = { ...body, ...params }; // moves the params to the body, replacing matching data
        req.body = NewRequestBody; // just to be safe. I believe you could omit this, but rather safe than sorry
        return next.handle(); // keep the request going
    }
}

So far so good. The ValidationPipe is global, with the whitelist: true and transform: true options.

ValidationPipeOptions = {
    transform: true,
    whitelist: true, // strip unknown fields
};

Setting up a local ValidationPipe with the same options does not help. The validation still sees the Start as a string.

"Start must not be less than 1",
"Start must not be greater than 50",
"Start must be a number conforming to the specified constraints"

Debug payload shows:

Description: 'Testing',
Start: '25'

Where Description was passed in the request body, so the interceptor is combining the data.

EDIT I can modify the interceptor to determine if the field in the body is numeric with something like:

intercept(context: ExecutionContext, next: CallHandler): Observable<any> 
{
    const req = context.switchToHttp().getRequest();
    const { params, body } = req; // get the params and body from req
    
    // Process through parameters checking values in request body
    for (const ParameterName in params) {
        if (typeof body[ParameterName] === 'number') {
            const Value: number = parseInt(params[ParameterName], 10);
            params[ParameterName] = Value;
        }
    }
    const NewRequestBody = { ...body, ...params }; // moves the params to the body
    req.body = NewRequestBody; // just to be safe. I believe you could omit this, but rather safe than sorry
    return next.handle(); // keep the request going
}

However, this only works in the values are in the request body. I still need to determine how to convert these for the validation with the @Type(()=>Number)

Steven Scott
  • 10,234
  • 9
  • 69
  • 117
  • That should work. [Here's a Stackblitz showing that `{start: '40' }` (the string) would indeed become `start: 40` (the number)](https://stackblitz.com/edit/typescript-9ayqwh?file=index.ts) – Jay McDoniel Sep 27 '22 at 20:47
  • 1
    I had the wrong import. I had Type from @nestjs/class-transformer, not class-transformer. – Steven Scott Sep 27 '22 at 21:07

0 Answers0