i.brod
i.brod

Reputation: 4623

How to pass constructor arguments, to a NestJS provider?

I have some Service, which requires a config object literal in its constructor, something like this:

@Injectable()
export class BatteriesService { 

    constructor(private config: Config) { }//The provider needs a config object 

}

If I simply add this class in the providers array of the module, I obviously get an error, being that a constructor argument is missing.

So, instead of just referencing the BatteriesService class, I need to somehow create an instance. I tried this:

@Module({
  controllers: [BatteriesController],
  providers: [{
    useFactory: ()=>{
     return new BatteriesService({'someProp': 'someValue'})
    },
    provide:'BatteriesService'
  }]
})

And this:

@Module({
  controllers: [BatteriesController],
  providers: [{
    useValue:new BatteriesService({'someProp': 'someValue'}),
    provide:'BatteriesService'
  }]
})

In both cases I get the following error:

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

How can this done, without "resorting" to bypassing the DI system, or creating another "inner" provider(config)?

Upvotes: 9

Views: 18185

Answers (1)

n1md7
n1md7

Reputation: 3479

When you need to have DI in service that has to be defined in the module.

In your case

@Injectable()
export class BatteriesService { 
  constructor(private config: ConfigService) { }
}

@Module({
  imports: [ConfigModule.forRoot({})], // Configure it as needed
  providers: [BatteriesService]
})

Your mistake is that you don't actually import ConfigModule while your service is dependent on it.

If you wish to use useFactory method then it would look like

@Module({
  providers: [{
    useFactory: (config: ConfigService) => {
      return new BatteriesService(config);
    },
    provide: BatteriesService,
    inject: [ConfigService]
  }]
})

I assumed your Config is actually Nest ConfigModule.

But if it's some custom Module you still need to import it as in the above examples.

If you want to pass an object literal as a config try this

interface MyConfigType = {
  something: string;
}
@Injectable()
export class BatteriesService { 
  constructor(@Inject('CONFIG') private config: MyConfigType) { }
}

@Module({
  providers: [{
    provide: 'CONFIG',
    useValue: {
      something: 'my-value'
    }
  }]
})

Upvotes: 14

Related Questions