Transform class to class/object (Entity to DTO) in TypeScript and NestS
Asked Answered
G

4

16

How to transform database entity User:

class User {
  public firstName: string;
  public lastName: string;
  public phone?: string;
  public email: string;
  public status: EUserState;
  public tokens: Token[];
  public password: string;
}

into DTO entity GetUserDTO:

class GetUserDTO {
  public id: number;
  public firstName: string;
  public lastName: string;
  public phone?: string;
  public email: string;
}

in TypeScript? I am using @nestjs, class-validator and class-transformer packages, but I not found any way to use them to achieve this.

Somebody can say, that having DTO is pointless for this, but we have DTOs shared between server and client to maintain API structure.

Any ideas?

Gybe answered 9/2, 2020 at 18:6 Comment(0)
D
26

There are couple of ways to achieve what you want

  1. You can manually map from Domain models to DTOs using static function from either the Domain or DTO
export class Domain {
   ...
   static toDTO(domain: Domain) {
      // mapping goes here
   }
}

or

export class Dto {
   ...
   static fromDomain(domain: Domain) {
      // mapping goes here
   }
}
  1. You can use a 3rd party library: automapper-ts, @wufe/mapper, @nartc/automapper (my lib), or morphism

class-transformer can also be considered a mapper, however, if you want to map from 1 model to another, then class-transformer can't really do that.

Dansby answered 10/2, 2020 at 1:58 Comment(3)
In clean architecture, the domain should not depend on the infrastructure layer, which means that the Domain class should not know about the DTO class. So the second option Dto.fromDomain should be preferred.Tallith
I recommend using @nartc/automapper, it works very well.Encrata
@Alwin07 now called @automapper/* or for example @automapper/core on NPM. Github remains under nartc org/user nameFlaminius
H
1

I use the following way for a lot of my projects:

Shared folder (it can be separated github repo, monore lib, npm etc.)

// user-response.dto.ts
class UserResponseDto {
    public readonly id: string;
    // other dto fields...

    // the simplest way is the same type as entity but the better one is to use own type (TUserData) instead
    constructor(data: UserEntity) {
        this.id = data.id;
    }
}

Backend side:

// user.entity.ts
@Entity('users')
class UserEntity {
   // your fields...
}

// users.controler.ts
// import { UserResponseDto } from '@shared';
@Controller('users')
class UsersController {
    @Get()
    public async getUsers(): Promise<UserResponseDto[]> {
        const data = await this.service.getUsers();

        return data.map(user => new UserResponseDto(user);
    }

    // Bonus: example of class-transform, class-validate
    @Post()
    @UsePipes(new ValidationPipe()) // you can use your own pipe, check: TransformPipe implements PipeTransform { ...implementation }
    public async createUser(
        @Body() dto: UserCreateDto,
    ): Promise<UserResponseDto> {}
}

Frontend side depends on chosen way but the concept is the same:

// users.api.ts
// import { UserResponseDto } from '@shared';
usersApi = {
    endpoints: {
        getUsers: (): UserResponseDto[]
    }
}
Hellgrammite answered 22/9, 2022 at 23:8 Comment(0)
O
0

first, change Entity into plain json by using classToPlain of class-transformer

then, change plain json into DTO by using plainToClass of class-transformer like below

  public async getAll(): Promise<ItemDTO[]> {
    return await this.repo.find()
      .then(items => items.map(e=>plainToClass(ItemDTO, classToPlain(e), { excludeExtraneousValues: true })));
  }

additionally, Let's use Exclude, Expose of class-transformer in the DTO for client data you want client to see.

@Exclude()
export class ItemDTO implements Readonly<ItemDTO> {
  @ApiProperty({ required: true })
  @IsUUID()
  @Expose()
  id: string;
}
Orb answered 23/3, 2020 at 15:16 Comment(0)
S
0

If you DTOs come from your database model, you can use a generator tools to create the DTOs from your schema

This lib have been made for generating DTOs and Entities from Prisma schema : https://github.com/robblovell/prisma-generator-nestjs/tree/main

it will not convert your entity, but it can make the work for you

Maybe there is some other lib that work using other ORM if you are not using Prisma, just for you to be aware of it !

Segregationist answered 31/8, 2023 at 14:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.