9

I am trying to create a new instance without breaking the DI layer, allowing my instance to have access to all injectable services it is using in its constructor

n example of what I have currently which does not allow me using any injectable services:

const camera: Camera = new Camera(id, options);

With this approach, the camera class could not import any injectable singletons or classes.

I have read here that you can use moduleRef to create a new instance, so I tried the following:

const camera: Camera = await this.moduleRef.create(Camera);

But the issue now is, I can't pass the ID and Options parameters, the only solution would be using a setter right after initializing it.

Question:

How can you create a new instance (not singleton) of a class, have it created by nest's injector and pass custom parameters on creation in the latest version of NestJS?

WalterV
  • 1,490
  • 2
  • 21
  • 33
Ben Beri
  • 1,101
  • 4
  • 23
  • 64

2 Answers2

9

Custom provider is what you need, you can find documentation here

If you want inject it in some of your modules try this way

@Module({
   ...
   providers: [
      {
         useFactory: (optProvider) => {
           return new Camera(optProvider.id, optProvider.options);
         }, 
         provide: Camera,
         import: [OptProvider] // pay attention that OptProvider is a valid provider in this module
      },
      SomeService
   ]
})
export class SomeModule {}

After it you can use provide this object via DI

export class SomeService() {
  constructor( protected readonly camera: Camera ) {}
}
Yevhenii
  • 1,565
  • 12
  • 27
  • 1
    Hey thank you for your time and answer. This still does not answer my question because with this way you can only inject existing injectables in the module but not a custom value passed via moduleRef! – Ben Beri May 20 '20 at 11:17
  • so you can use this approach to build your own provider that you can inject using nest DI and transmit objects with it. Not sure that nest allows inject/remove something right it moduleRef because it could be more complex than just array with objects. It could have unobvious dependencies – Yevhenii May 20 '20 at 11:26
8

We had the same issue. The way we solved it is by using the useFactory, and instead of returning the value, we retuned a factory function that excepts the extra arguments and we use it to create new instances. Like so:

   const factory = {
  provide: PUPPETEER_FACTORY,
  useFactory: (configService): PuppeteerFactory => {
    return {
      create: function(config?: PuppeteerConfig) {
        return new Puppeteer(configService, config);
      }
    };
  },
  inject: [ConfigService]
};

and than you simply use the create function that is returned, like so:

constructor(@Inject(PUPPETEER_FACTORY) private puppeteerFactory: PuppeteerFactory) {}
this.puppeteerInstance = this.puppeteerFactory.create({
      timezone: "UTC+8",
      userAgent: "BLA"
    });

Notice that PUPPETEER_FACTORY is a const

Lior Alon
  • 163
  • 1
  • 6