typestack / class-transformer

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

feature: dependecy injection #461

Open Arnaud-Dev-Nodejs opened 3 years ago

Arnaud-Dev-Nodejs commented 3 years ago

Description

To follow the previous issue : https://github.com/typestack/class-transformer/issues/153 Actually using NestJS, I create a new TransformModifier like this :

import { TransformOptions } from "class-transformer";
import { defaultMetadataStorage } from "class-transformer/storage";

export function Deserialize(
  separator: string | RegExp = " ",
  options: TransformOptions = {}
): PropertyDecorator {
  const transformFn = (value: any, obj: any) => String(value).split(separator);

  return function (target: any, propertyName: string | Symbol): void {
    defaultMetadataStorage.addTransformMetadata({
      target: target.constructor,
      propertyName: propertyName as string,
      transformFn,
      options
    });
  };
}

but I require to inject ConfigService in it to get the 'separator' input to do something like that

export function Deserialize(
  appType: string | RegExp = " ",
  options: TransformOptions = {}
): PropertyDecorator {
  const separator = this.config.get<AppConfig>("MyClientConfig").splitSeperator;
  const transformFn = (value: any, obj: any) => String(value).split(separator);

  return function (target: any, propertyName: string | Symbol): void {
    defaultMetadataStorage.addTransformMetadata({
      target: target.constructor,
      propertyName: propertyName as string,
      transformFn,
      options
    });
  };
}

the problem is at the moment of the initialization of the Decorator, the data of the config are not up. So I have to inject it.

Proposed solution

class-validator offers a solution ; they add a "useContainer" functionnality like this :

 useContainer(app.select(AppModule), { fallbackOnErrors: true });

and allow in class to do that :

  constructor(@Inject('ConfigService') public readonly config: ConfigService) {
    super();
  }
NoNameProvided commented 3 years ago

This is already possible with TypeDI. You can simply get any value from Container.get from TypeDI.

NoNameProvided commented 3 years ago

Actually it would be nice to support other containers than TypeDI. Leaving it open.

gterras commented 1 year ago

Would be really nice to be able to use Nest DI with @Transform decorators.

rafaelgc commented 1 year ago

In my case, I'd like to be able to access the database from the @Type decorator. I'm not sure if the proposal of @Arnaud-Dev-Nodejs would cover my case.

@Type((data: TypeHelpOptions) => {
    const discriminator = getFromDb();
    switch (discriminator) {
      case DeliveryTypes.DELIVERY:
        return DeliveryShipmentDto;
      case DeliveryTypes.PICKUP:
        return PickupShipmentDto;
    }
  })
@ValidateNested()
  shipment: DeliveryShipmentDto | PickupShipmentDto;
joao-moonward commented 11 months ago

It's been some time since we last talked about this subject. Have there been any advancements or new information? Personally, I use Serialization to transform my objects without altering their type within the service scope. Specifically, I have a pre-signed URL service and I want to inject it into the transformer to customize the fields that need conversion. The solution I have so far is Interceptor

ailequal commented 1 month ago

It's been some time since we last talked about this subject. Have there been any advancements or new information? Personally, I use Serialization to transform my objects without altering their type within the service scope. Specifically, I have a pre-signed URL service and I want to inject it into the transformer to customize the fields that need conversion. The solution I have so far is Interceptor

Interceptors might actually do the job as a workaround (we have access to the DI), but they are bind to a specific set of routes, while a DTO with a custom decorator that implements class-transformer would be more independent.

ailequal commented 1 month ago

In the end for my specific use case, I used a custom pipe. It provides access to the DI and it can be easily applied to where I need. It still isn't a "global" approach compared to a DTO with a custom transformer inside, but good enough for me.