Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NestJS create a new instance with custom parameters staying in the dependency injection layer

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?

like image 585
Ben Beri Avatar asked May 14 '20 10:05

Ben Beri


People also ask

How does dependency injection work in NestJS?

Dependency injection is an inversion of control (IoC) technique wherein you delegate instantiation of dependencies to the IoC container (in our case, the NestJS runtime system), instead of doing it in your own code imperatively.

What is injectable in NestJS?

@Injectable() is how you tell Nest this is a class that can have dependencies that should be instantiated by Nest and its DI system. The code you posted works because there are no injected dependencies.

Are NestJS services singletons?

NestJS Modules are Singletons by default. Their Providers are also singletons when they are provided from within the same module. However, there are cases where we want to use just a single solo class as a provider to a couple or more modules.

What is @body in NestJS?

To instruct Nestjs that we need the body values from the request, we should use the @Body() decorator function from the @nestjs/common module before the body parameter. Doing this will bind the body values from the request to the body parameter in the createPost() method.


2 Answers

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 ) {}
}
like image 117
Yevhenii Avatar answered Oct 19 '22 18:10

Yevhenii


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

like image 36
Lior Alon Avatar answered Oct 19 '22 20:10

Lior Alon