Nest: Hierarchical Injection works not as expected

Created on 30 May 2018  路  6Comments  路  Source: nestjs/nest

I'm submitting a...


[ ] Regression 
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

I have a module with one controller, that needs a config injected:

@Module({
    imports: [],
    controllers: [AController],
    providers: [ /*ModuleAConfigProvider*/],
})
export class ModuleA {
}

And I have an ApplicationModule which imports the upper module:

@Module({
    imports: [ModuleA],
    providers: [ModuleAConfigProvider],
})
export class ApplicationModule {
}

When commenting out ModuleAConfigProvider in the providers of ModuleA nest will complain Nest can't resolve dependencies of the AController. Remove the comment marks and it works.

Expected behavior

When a provider in an imported module is missing it should go up the module tree and find the next matching provider which would be ModuleAConfigProvider of the ApplicationModules providers.

Angular does the same: Injector Bubbling

One difference in Angular is, that it does the dependency injection on component level.

What is the motivation / use case for changing the behavior?

The motivation is that the imported module could be imported in different applications which can inject different dependencies.

Environment

Nest version: 5.0.1

For Tooling issues:

  • Node version: 9.5.0
  • Platform: Mac
question 馃檶

Most helpful comment

Your implementation is very unclear.

The way Nest Dependency Injection works is that the exports in the Module will be made available to the outside world and when you import the Module, you will have access (aka Injectables) to all the Providers in the exports array.

Your issue will be solved if you put ModuleAConfigProvider in the exports of ApplicationModule then import ApplicationModule in ModuleA. However, that would create a Circular Dependency.

My suggestion would be abstracting all the Config providers to a separate module, maybe ConfigProviderModule and implement the logic to provide the Config for different Modules there. Put all the Providers in providers array and exports array. Then import ConfigProviderModule in ModuleA, ModuleB, ModuleC etc... to have the ModuleAConfigProvider available.

All 6 comments

What about trying to add it - ModuleAConfigProvider - also to exports: [], in the ApplicationModule, it should work.

It must be on exports, so can the out side World see it.

No that did not work for me. Still getting the same error.

Hi @vinnichase,
Nest injector works somewhat different than angular one. We don't expose providers globally by default. Instead, all of them are encapsulated inside the module and you have to explicitly export providers. This is an intended behavior explained in the Modules section, here.

Hi @kamilmysliwiec ,

thanks! Yes I got that point and i wanted to edit my last post yesterday to the following: I exposed ModuleA also with .forRoot(config) and return it with the ModuleAConfigProvider which is fed with useValue: config. This gives me the modularity that I would expect from an IOC container.

However inversion of control means for me that the parent module owns the decision whether or not to override dependencies of the child module, where I think the angular default behavior comes from. I know you provide this possibility by explicitly exporting the provider in ModuleA which somehow did not work for me. When I add the provider in providers[] in ModuleA it will inject this one even if its exported and reprovided in the ApplicationModule.

I was not sure if I should post this question as a bug report or a question. So could you evaluate if your intended behavior actually works and I would edit the issue afterwards.

@shekohex you propably meant to add ModuleAConfigProvider to exports: [] in ModuleA right? In ApplicationModule it makes no sense since its the root module.

Your implementation is very unclear.

The way Nest Dependency Injection works is that the exports in the Module will be made available to the outside world and when you import the Module, you will have access (aka Injectables) to all the Providers in the exports array.

Your issue will be solved if you put ModuleAConfigProvider in the exports of ApplicationModule then import ApplicationModule in ModuleA. However, that would create a Circular Dependency.

My suggestion would be abstracting all the Config providers to a separate module, maybe ConfigProviderModule and implement the logic to provide the Config for different Modules there. Put all the Providers in providers array and exports array. Then import ConfigProviderModule in ModuleA, ModuleB, ModuleC etc... to have the ModuleAConfigProvider available.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

marshall007 picture marshall007  路  3Comments

yanshuf0 picture yanshuf0  路  3Comments

cojack picture cojack  路  3Comments

anyx picture anyx  路  3Comments

menme95 picture menme95  路  3Comments