Nestjs serialization in validation pipe not working

150 views Asked by At

I am trying to use @Expose with an alias name for properties of a class as below but I am getting the response in camelCase.

import { Expose } from 'class-transformer';
import { BaseResponse } from './base-response.class';

export class ValidationResponse extends BaseResponse {
    @Expose({
        name: 'SomeField',
    })
    someField: string;

    @Expose({
        name: 'AnotherField',
    })
    anotherField= string;
}

I am throwing a bad request error as below with ValidationResponse from a validation pipe in case of payload validation failure as below.

Bad request error class

    import { HttpException, HttpStatus } from '@nestjs/common';

    export class BadRequestError extends HttpException {
      constructor(response?: unknown) {
        super(response, HttpStatus.BAD_REQUEST);
      }
    }

Custom validation pipe

  import { ArgumentMetadata, Inject, PipeTransform, UseInterceptors, ClassSerializerInterceptor } from '@nestjs/common';
  import { plainToClass } from 'class-transformer';
  import { validate } from 'class-validator';
  import { BadRequestError } from '../exceptions/bad-request.exception';
  import { ErrorModel } from '../../common/classes/error-model.class';
  import { ErrorCode } from '../../common/enums/error.enum';
  import { ValidationResponse } from '../../common/classes/validation-response.class';
  import { REQUEST } from '@nestjs/core'
  import { CustomRequest } from 'src/common/classes/custom-request.class';

    @UseInterceptors(ClassSerializerInterceptor)
    export class CustomValidationPipe implements PipeTransform {
        constructor(@Inject(REQUEST) protected readonly request: CustomRequest) { }
    
        async transform(value: any, { metatype }: ArgumentMetadata) {
            const object = plainToClass(metatype, value);
            const errors = await validate(object, {
                forbidUnknownValues: false,
            });
            if (errors.length) {
                let errorResponse: ValidationResponse = new ValidationResponse();
                errorResponse.someField= 'somevalue';
                errorResponse.anotherField= 'anothervalue';
                throw new BadRequestError(errorResponse);
            }
            return value;
        }
    }

I am using the validation pipe in controller for a POST API as below

import { CustomValidationPipe } from 'custom-validation.pipe';

@UseInterceptors(ClassSerializerInterceptor)
export class SomeController {

@Post('test')
    @HttpCode(200)
    @ApiCreatedResponse({
        type: SomeResponse,
    })
    async someFunction(
        @Request()
        request,
        @Body(CustomValidationPipe)
        body: SomeRequest,
    ): Promise<SomeResponse> {
      // controller logic here
   }
}

With @Expose I am trying to change the casing of the fields to pascal case but when I get the response from the pipe it is in camel case as the field are originally defined in ValidationResponse class. Maybe I am doing something wrong. Any help is really appreciated.

1

There are 1 answers

2
Jay McDoniel On

The ClassSerializerInterceptor only works on returned values from the controller, not errors/exceptions or anything thrown, at least by default. If you want to add that, you'd have to extend the class and add a way to .catchError() for when that happens. It would be easier for you to just set the ValidationResponse class to have these pascal case fields you want it to and set them directly when throwing the exception.

Also as a side note, the @UseInterceptors() does nothing on the pipe custom pipe class. It only effects the controller here, so you can get rid of it.