[ ] Regression
[ ] Bug report
[x] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.
A module can import other modules, but only statically when the app is started.
Modules should be able to declare dependencies on other providers/modules, so that they are injectable when the module is created.
I would like to setup my database providers (using the TypeOrmModule) according to a config. That config is exposed through a provider.
Right now I do not see a way of using the config provider to set up TypeOrmModule.forRoot(). Instead I use a hack where I set global.config and inject that global both into ConfigModule and TypeOrmModule.forRoot(global.config.typeOrm). Obviously, this is not a nice way of injecting it.
The issue gets even more complicated in an E2E test where I need to test two servers at once. I start up only one (mock) db, meaning that each server opens a new connection to it. TypeOrm complains that they both try to use the connection name 'default', so I have to configure each of the servers to use a different connection name.
However, now I need to specify that connection name not only in forRoot(), but also when I import the individual repositories with TypeOrmModule.forFeature([User], 'userConnection'). So when I change the connection name in the config, I will have to manually change the forFeature() as well, or use the nasty global.config hack.
I have found that providers can inject dependencies. But because forFeature() returns multiple providers, I cannot easily wrap it with something like:
...
providers: [
{
provide: TypeOrmModule,
useFactory: (config) => {TypeOrmModule.forFeature(config.typeOrm),
inject: [ConfigProvider]
}
]
...
Similarly for forRoot(). So the issue seems to be that I want to make a module depend on a provider.
I don't know if such a dependency between a module and a provider is actually a good idea. And I can even imagine that Nest needs the modules to be free of such dependencies. But I don't see another way, and I think that this use case with the config is very strong, since a similar issue is being discussed in #671.
Another option would be to have a special context of sorts that would be valid during setup, in which you could put the config. Then all modules that need the config would access the context, including the ConfigModule, which can then setup the config provider to be used from this point on.
In any case I mainly wanted to initiate a discussion about such a feature.
Totally agree: this is bad design. I'm not sure if Nest providers (@Injectable) are global now, but the proper way to do this in Angular would be to put the module in your imports array, then Provide ConfigProvider as an injection token of that module. Perhaps this needs to be redesigned.
@wbhob What is bad design? Injecting a provider into a module (thus making the module depend on a provider) or not having that option?
The issue I'm having is that I can't pass the config from ConfigProvider to the TypeOrmModule, because the latter needs all the options before it is initialized. Therefore, even with the technique you described, I would need a way to defer the initialization of TypeOrmModule until after ConfigProvider is fully set up. I don't know how to do this in Nest.
Bad design would be to make a module depend on the user's input of a provider like the way you demonstrated.
A solution would be to use forwardRef to defer instantiation until after the ConfigProvider was created and resolved.
Docs: https://docs.nestjs.com/fundamentals/circular-dependency
@wbhob That might very well be it! I will try the forward references.
@wbhob I can't figure out how to access the ConfigModule's provider in the forwardRef. My attempt is published in the forwardRef branch of this repo. Could you maybe take a look and ideally submit a PR with your suggestion?
Sorry, I'm not very familiar with this module in particular. However,
these docs don't even mention a TypeORMModule: https://docs.nestjs.com/recipes/sql-typeorm. Could this be a problem with your implementation?
@wbhob I followed the doc about databases which uses the TypeOrmModule.
But this problem is not limited to that module. It's a general problem when you're using dynamic modules that should depend on a configuration. I cannot figure out how to pass that config cleanly to the provider and then have all my app's modules use that config provider.
I would suggest using dedicated ConfigService instance that is created manually, but bounded to the DI container using useValue: configService. Not everything has to be instantiated by Nest itself, especially in the case where modules need certain properties as an 'input'. The dynamic modules that depend on providers is definitely a very bad practice from the design perspective.
@kamilmysliwiec You can provide an example for this practical case?
@cdiaz In #530 it was proposed to pass in the config via dynamic modules. However, because of a bug (#671) you currently cannot pass a dynamic module to NestFactory.create(), so for now you need to be creative with a workaround such as shown in my demo repo.
The method from #530 is actually ok, and it does not need the dependency described in this issue.
It still means that there practically "two" configs, because the modules will need to have the "raw" config fed into them, and cannot access the ConfigProvider. But that "duplication" is manageable.
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.