Convert stringified JSON to Object using class-transformer

7.7k Views Asked by At

There is a nest.js project, where in the request body we expect an object, one property of this object contains stringified JSON value. The idea is to convert this string to an object, validate it and pass to controller as an object ValidationPipe set up:

app.useGlobalPipes(
  new ValidationPipe({
   whitelist: true,
   transform: true,
  }),
);

DTO:

@Transform(parseJson, { toClassOnly: true })
@Type(() => AdditionalInfo)
@IsNotEmptyObject()
@ValidateNested()
additionalInfo: AdditionalInfo;

parseJson function

export function parseJson(options: {
  key: string;
  value: string;
  obj: string | Record<string, any>;
}): Record<string, any> {
  try {
    return JSON.parse(options.value);
  } catch (e) {
    throw new BadRequestException(`${options.key} contains invalid JSON `);
  }
}

For some reason in the controller the parsed value gets lost, and we receive an empty object.

1

There are 1 best solutions below

0
On BEST ANSWER

Looks like @Transform works well with primitives only. Decided to create ParseJsonPipe and use it instead. Usage (in the controller):

@Body('additionalInfo', new ParseJsonPipe(), new ValidationPipe(AdditionalInfoDto)) additionalInfo: AdditionalInfo,

ParseJsonPipe:

import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from '@nestjs/common';

@Injectable()
export class ParseJsonPipe implements PipeTransform<string, Record<string, any>> {
  transform(value: string, metadata: ArgumentMetadata): Record<string, any> {
    const propertyName = metadata.data;
    try {
      return JSON.parse(value);
    } catch (e) {
      throw new BadRequestException(`${propertyName} contains invalid JSON `);
    }
  }
}

ValidationPipe implements PipeTransform from @nestjs/common, transform function looks like that:

async transform(value: any): Promise<any> {
    if (!this.metaType) { // AdditionalInfoDto
      return value;
    }
    const object = plainToClass(this.metaType, value);
    const errors = await validate(object);
    if (errors.length > 0) {
      const message = this.getErrorMessages(errors);
      throw new BadRequestException({ message });
    }
    return value;
}