map multiple properties to single nested object using typescript and class-transformer

Viewed 1040

I'm using class-transformer (in nestjs) to convert my database entities into dto types to output from my api.

My database entities look like:

class MyEntity {
  id: string;
  property1: string;
  property2: string;
}

but my API needs to output something like

class NestedDto {
  property1: string;
  property2: string;
}

class MyDto {
  id: string;
  nested: NestedDto;
}

So, using class-transformer, how do I combine those two properties into a single nested object?

I've tried using @Transform like this:

class MyDto {
  @Expose()
  id: string;

  @Transform(p => ( property1: p.obj.property1, property2: p.obj.property2 }));
  nested: any;
}

it technically works, but it doesn't scale and isn't very clean.

Is there a nicer way to achieve this using class-transformer?

Also, for bonus points, if those two properties are null in the database, it would be nice for the nested property on the api to not exist - though this is nice-to-have, and not a requirement :)

3 Answers
class MyDto {
  @Expose()
  id: string;

  @Transform((p: MyEntity) => ( property1: p.obj.property1, property2: p.obj.property2 }));
  @IsOptional()
  nested: NestedDto;
}

YOLO I never used nestjs

  @Transform(({ value, obj }) => {
value = new Card();
value.expiration = obj.expiryDate;
return value;}) productForeignCard: Card;

try this

I had the same problem, I wanted to map a geoJson data to a more humain friendly one {'long':..., 'lat':...}, which means map the input data and the output data. I apply this as an input DTO to convert the schema above into geojson.

@ValidateNested({ each: true }),
@Type(() => DBLocationDto),
@Transform(
  ({ value }) =>
    morphism(
      {
        type: () => 'Point',
        coordinates: iteratee => [iteratee.lng, iteratee.lat],
      },
      value,
      HumainLocationDto,
    ),
  { toPlainOnly: true }
)

Then in my output DTO I have almost the same, in the opposite

@Type(() => HumainLocationDto),
@Transform(
  ({ value }) =>
    morphism(
      {
        lng: iteratee => iteratee.coordinates[0],
        lat: iteratee => iteratee.coordinates[1],
      },
      value,
      DBLocationDto,
    ),
  { toClassOnly: true },
)

As you can see I am using morphism to map my to classes, but you can do it directly by yourself.

PS: I had an issue when the location is not present at all, it was considered as if it was optional, I fixed it by forcing the definition with @IsDefined()

Related