How to transform nested object with class-transformer's @Transform

87 views Asked by At

I have entity object User defined like:

@Entity({
  name: 'user',
})
export class User extends RootBaseEntity {
  @Column({ type: String })
  uid: string;

  @Index()
  @Column({ type: String, unique: true })
  email: string;

  @Column()
  @Exclude({ toPlainOnly: true })
  password: string;

  @Index()
  @Column({ type: String, name: 'first_name' })
  firstName: string | null;

  @Index()
  @Column({ type: String, name: 'last_name' })
  lastName: string | null;

  @ManyToOne(() => Role, (role) => role.users, {
    eager: true,
  })
  @JoinColumn({ name: 'role_id', referencedColumnName: 'id' })
  role: Role;

  @ManyToOne(() => Status, (status) => status.users, {
    eager: true,
  })
  @JoinColumn({ name: 'status_id', referencedColumnName: 'id' })
  status: Status;
}

Role and Status are also entities defined like:

@Entity({
  name: 'role',
})
export class Role extends RootBaseEntity {
  @Column({
    type: 'enum',
    enum: RoleEnum,
    name: 'name',
  })
  @Index({
    unique: true,
  })
  name: string;

  @OneToMany(() => User, (user) => user.role)
  @JoinColumn({ referencedColumnName: 'id' })
  users: User[];
}

,

@Entity({
  name: 'status',
})
export class Status extends RootBaseEntity {
  @Column({
    type: 'enum',
    enum: StatusEnum,
    name: 'name',
  })
  @Index({
    unique: true,
  })
  name: string;

  @OneToMany(() => User, (user) => user.status)
  @JoinColumn({ referencedColumnName: 'id' })
  users: User[];
}

Example of entire User entity:

{
    id: 1,
    createdAt: 2024-01-31T13:20:42.910Z,
    updatedAt: 2024-01-31T13:20:42.910Z,
    deletedAt: null,
    uid: '07ed21c5-f183-49ac-bba9-433118cf20cb',
    email: '[email protected]',
    password: '$2a$10$Ts0y.5CdnTRsP6Yy2ZZJsunNiqfRej9oNSFw7wmr.DK9z7SoFmWou',
    firstName: 'Super',
    lastName: 'Admin',
    role: {
      id: 2,
      createdAt: 2024-01-31T13:20:42.783Z,
      updatedAt: 2024-01-31T13:20:42.783Z,
      deletedAt: null,
      name: 'admin'
    },
    status: {
      id: 1,
      createdAt: 2024-01-31T13:20:42.797Z,
      updatedAt: 2024-01-31T13:20:42.797Z,
      deletedAt: null,
      name: 'active'
   }
}

I want to transform User entity to UserDto, which should in the end look like this:

{
    uid: "a103234b-1b46-414b-a380-7090d8939b4f",
    email: "[email protected]",
    firstName: "John",
    lastName: "Doe",
    role: 1,
    status: 1,
    createdAt: "2024-01-31T13:20:43.007Z"
}

Properties role and status should be equal to Role.id and Status.id.

What I currently have defined as UserDto:

@Exclude()
export class UserDto {
  @Expose()
  @IsString()
  uid: string;

  @Expose()
  @IsString()
  email: string;

  @Expose()
  @IsString()
  firstName: string;

  @Expose()
  @IsString()
  lastName: string;

  Expose()
  @Type(() => RoleDto['id'])
  @Transform(({ value }) => {
    console.log(value);

    return value.id;
  })
  role: RoleDto['id'];

  @Expose()
  @Type(() => StatusDto)
  @Transform(({ value }) => value.id)
  status: StatusDto['id'];

  @Expose()
  @IsDate()
  createdAt: Date;
}

RoleDto and StatusDto are defined like:

@Exclude()
export class RoleDto {
  @Expose()
  @IsNumber()
  id: number;
}

,

@Exclude()
export class StatusDto {
  @Expose()
  @IsNumber()
  id: number;
}

The problem: In the @Transform decorator, when I return value.id and then console.log(value), I get two logs:

1.

{
  id: 2,
  createdAt: 2024-01-31T13:20:42.783Z,
  updatedAt: 2024-01-31T13:20:42.783Z,
  deletedAt: null,
  name: 'admin'
}
2

And since I'm returning value.id, in the second iteration there is no more id property, but the value is just plain number (the actual id), and thus the role is set to undefined.

How to work around this? What am I missing here? Is there a better solution to this simple conversion from User to UserDto? Thanks

1

There are 1 answers

0
Vimbi On

You need to add a property to the transformation decorator toClassOnly.

@Transform(
    ({ value })  => value.id,
    {
      toClassOnly: true,
    },
  )

Here's an explanation from the developer https://github.com/nestjs/nest/issues/3842#issuecomment-575041486