#How to wrap a package and use dependency injection with it ?

10 messages · Page 1 of 1 (latest)

native sphinx
#

Hello, i have been strugling with this case.

I'm using some NPM package, and i would like to have some kind of wrapper. To do that, i created a provider, and i intend to use it with dependency injection. So that means, importing it as a Module in some .module.ts file, and use it in my .service.ts file.

But this package first needs to be initialized by passing it some tokens, API Key, or whatever. So i though about populating the class example here :

https://docs.nestjs.com/fundamentals/custom-providers#class-providers-useclass

With some constructor, and i added methods like this :

import Package from "package"

@Module({
  providers: [{ provide: "customProvider", useClass: CustomProvider }]
  exports: ["customProvider"]
})
export default class CustomProvider {
  public constructor(private readonly package: Package) {
    package = new Package({
      api_key: ""
      token: ""
    })
  }

  public methodWrapped() {
    // some process to be abstracted so that the service just have to call this function to work with the NPM package
  }
}

So that in my service (where it will be injected), i could call the provider like this :

export class ExampleService() {
  constructor(@Inject(CustomProvider) private readonly customProvider: CustomProvider) {}

  function function1() {
    // other things occuring before
    this.customProvider.methodWrapped()
    // and other things also occuring after this customProvider is called
  }
}

But it doesn't seems to work. When importing the custom provider in my module i get the following error :

Error: Nest can't resolve dependencies of the CustomProvider (?). Please make sure that the argument a at index [0] is available in the CustomProvider context.

I think the error is occuring because it is somehow trying to inject the package inside CustomProvider but i'm not sure.

I'm also not sure that providers is what i need here, but i don't know any other way to make another class / file / whatever dependency injectable.

Any help appreciated !

#

How to wrap a package and use dependency injection with it ?

mint folio
#

Why not make the wrapper inject an instance of the package and use a facotry to instantiate the package before it gets injected into the wrapper class? Something like

@Module({
  providers: [
    {
      provide: 'WRAPPER',
      useClass: WrapperClass,
    },
    {
      provide: 'INSTANCE',
      useFactory: () => {
          return new Package(options);
      }
    }
  ],
  exports: ['WRAPPER'],
})
export class WrapperModule {}

@Injectable()
export class WrapperClass {
  constructor(@Inject('INSTANCE') private readonly instance: Package) {}
}
#

Also, now with the INSTANCE factory, you can inject the ConfigService as needed and for testing the WrapperClass you can provide a full on mock of the Package instance rather than needing to use jest.mock() or anything else

native sphinx
#

Oh that's smart ! Haven't though about splitting the module and the class having a constructor

#

So, if i get it, the WrapperClass is only used internally by the module, i still need to import this same module in my file.module.ts, and put it in the constructor in my file.service.ts right ?

#

For now here is what i have :

my wrapper :

@Injectable()
export class MailjetClass {
  public constructor(@Inject('mailjetInstance') private readonly mailjetInstance: Mailjet) {}

  public async sendEmail(): Promise<boolean> {
    // Do some things to facilitate email sending
  }
}

@Module({
  providers: [
    {
      provide: 'mailjet',
      useClass: MailjetClass,
    },
    {
      provide: 'mailjetInstance',
      useFactory: (): Mailjet => {
        return new Mailjet({
          apiKey: "string",
          apiSecret: "string",
        });
      }
    }
  ],
  exports: ['mailjet'],
})
export class MailjetModule {}

In my module i import it like that :

@Module({
  imports: [MailjetModule],
  controllers: [TestController],
  providers: [TestService],
})
export class TestModule {}

And in my service i inject it like that :

@Injectable()
export class TestService {
  public constructor(
    @Inject("mailjet")
    private readonly mailjetProvider: MailjetModule
  ) {}

  public async sendEmailForUser(
    userId: number,
  ): Promise<boolean> {
    // TODO
  }
}
#

As it is, it compiles successfully, but i don't have access to my MailjetClass method in my module in my service.

mint folio
#

In your TestService that type should be MailjetClass

native sphinx
#

That was it, thanks !