typestack / class-transformer

Decorator-based transformation, serialization, and deserialization between objects and classes.
MIT License
6.64k stars 486 forks source link

question: Is class-transformer really thought to be used in the backend? #1674

Closed markoj3s closed 1 month ago

markoj3s commented 5 months ago

Basically, back-end code usually manipulates ORM objects and transforms them to return suitable data to the clients. However, class-transformer does not seem to work well with ORM objects (at least not with Sequelize models' instances); also, plainToInstance does not seem to include in the transformed object fields defined in getters like:

@Expose()
get name() {
  return this.firstName + ' ' + this.lastName;
}

So at first glance it feels more suitable to simply work as follows in the back-end:

export interface UserDTO {
  firstName: string;
  lastName: string;
  screenName: string;
}

export function fromUser(user: User): UserDTO { // `User` class being the ORM Model
  return {
    firstName: user.firstName,
    lastName: user.lastName,
    screenName: `${user.firstName} {user.lastName}`;
  };
}

However, it seems to work well with front-end code which receives json objects.

So I was wondering if I was doing something wrong, or if this library is indeed thought for the front-end rather than the back-end?

markoj3s commented 4 months ago

CC @NoNameProvided

ceigey commented 1 month ago

Just "driving past" but saw this when I went to search for "performance" (somehow).

I think it's meant for the backend, but definitely on the "user-data facing" side. I wouldn't consider that frontend.

I think the main niche this library is used for is handling incoming data (e.g. from users, API consumers, etc), which might come in as JSON, then need to be transformed to a class form (and then either validated with class-validator or with something else). So using it more for DTOs would be expected. It's probably expected your DB data is already validated or typesafe (SQL, Mongoose), or is being accessed as JSON (plain MongoDB driver) in which case you can use class-transformer + class-validator anyway.

(A less opinionated equivalent might be Zod. In other languages, there is a similar split in responsibilities with e.g. Java and Kotlin, where you might have one library handling serialisation of DTOs (e.g. Kotlinx Serialization, Jackson), and then another library (e.g. Spring Data) handling the ORM models. This handles roughly the same niche as Kotlinx, but without the AOT compilation benefits).

Basically, taking unknown, plain objects, and turning them into something richer with methods, state, or encapsulation as required.

Re getters and setters, makes sense that you can't use plainToInstance to populate something with a getter, you would need a corresponding setter. The other way should work though, e.g. instanceToPlain or instanceToInstance. The getter has to be accessible from the "left hand side" of the operation basically.

I guess you can approach this in a number of ways and potentially make this quite complicated, like the below:

export class UserDTO {
  @Transform(({ value, obj }) => value ?? obj.screenName.split(" ")[0])
  firstName: string;

  @Transform(({ value, obj }) => value ?? obj.screenName.split(" ")[1])
  lastName: string;

  @Expose()
  get screenName() {
    return `${user.firstName} {user.lastName}`;
  }
}

export function fromUser(user: UserModel): UserDTO {
  return plainToInstance(UserDTO, {
    firstName: user.firstName,
    lastName: user.lastName,
    screenName: `${user.firstName} {user.lastName}`;
  });
}
diffy0712 commented 1 month ago

@ceigey thank you for your detailed answer, I aggree. This library can be used at the backend or frontend, where you find it suitable. If you have good tests you should be fine.

Although, if you want class-transformer to work nicely with an ORM, you will have to write some custom Transformers as the integration with other libraries is out of the scope of this library and will not implement anything related to any ORM.

diffy0712 commented 1 month ago

Closing as answered. If you have any questions left feel free to comment on this issue.

github-actions[bot] commented 2 weeks ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.