Repo is available here to highlight the issue.
I am having a problem with a race condition. I have created a ConfigModule
- this has a forRoot
and a forChild
.
The forRoot
sets up the loading of the .env
file and the forChild
uses it in another module.
The problem is that forChild
is called before forRoot
. The ConfigService
would be injected with missing config because forRoot
hasn't executed first.
> AppModule > ConfigModule.forRoot InstanceModule >
> ConfigModule.forChild
I placed some simple console.log
mands that output this
I am in Config Module FOR CHILD
I am in Config Module FOR ROOT
As you can see the forChild
is being executed first, I tried using forwardRef
and that didn't work.
If you let the application run you will see
[2019-03-24T11:49:33.602] [ERROR] ConfigService - There are missing mandatory configuration: Missing PORT
[2019-03-24T11:49:33.602] [FATAL] ConfigService - Missing mandatory configuration, cannot continue!, exiting
This is because I check that some process.env
are available which are loaded in via dotenv
. Of course, because the forRoot
isn't executed first then the forChild
returns its own new instance of the ConfigService
.
ConfigService
validates the availability of the environment variables.
So, basically, the forChild
is executing and returning its own ConfigService
before forRoot
.
TO make it work, if you ment out the InstanceModule
inside the AppModule
then it will automatically start listening and returns the port number from an environment variable.
Of course, because InstanceModule
uses the forChild
- there is a race condition.
Repo is available here to highlight the issue.
I am having a problem with a race condition. I have created a ConfigModule
- this has a forRoot
and a forChild
.
The forRoot
sets up the loading of the .env
file and the forChild
uses it in another module.
The problem is that forChild
is called before forRoot
. The ConfigService
would be injected with missing config because forRoot
hasn't executed first.
> AppModule > ConfigModule.forRoot InstanceModule >
> ConfigModule.forChild
I placed some simple console.log
mands that output this
I am in Config Module FOR CHILD
I am in Config Module FOR ROOT
As you can see the forChild
is being executed first, I tried using forwardRef
and that didn't work.
If you let the application run you will see
[2019-03-24T11:49:33.602] [ERROR] ConfigService - There are missing mandatory configuration: Missing PORT
[2019-03-24T11:49:33.602] [FATAL] ConfigService - Missing mandatory configuration, cannot continue!, exiting
This is because I check that some process.env
are available which are loaded in via dotenv
. Of course, because the forRoot
isn't executed first then the forChild
returns its own new instance of the ConfigService
.
ConfigService
validates the availability of the environment variables.
So, basically, the forChild
is executing and returning its own ConfigService
before forRoot
.
TO make it work, if you ment out the InstanceModule
inside the AppModule
then it will automatically start listening and returns the port number from an environment variable.
Of course, because InstanceModule
uses the forChild
- there is a race condition.
- Can you post the link to your repo? – Kim Kern Commented Mar 24, 2019 at 12:56
- 1 Sorry my bad, I forgot to paste it :-) Updated it now. – Ian Gregson Commented Mar 24, 2019 at 12:58
1 Answer
Reset to default 8Why does this not work?
1) Nest builds up a dependency graph and instantiates the given modules and their providers according to this graph. The order of your imports or the naming of your dynamic modules's methods (forRoot
/forChild
) does not influence the order of the instantiation.
2) When you create dynamic modules, each module will be its own instance, they won't be singletons like regular modules. In your case, you'd create two different ConfigModule
instances and with it, two different ConfigService
instances; hence they won't share your .env
configuration. This can't work, independent of the instantiation order.
Alternatives
Have a look at the nestjs/typeorm
package. Under the hood, it creates a shared TypeOrmCoreModule
, which is shared between the different dynamic module instances created by TypeOrmModule.forRoot
/ TypeOrmModule.forChild
. For it to be shared and dynamic at the same time, it has to be made @Global
though. In your case, since you don't have any configurations in your forChild
imports, you would just make the whole ConfigModule
global and then omit the forChild()
imports, since the ConfigService
would be globally available anyway.
If you don't want your service to be globally available, you could initialize your service after the startup process, for example in your AppModule
's onModuleInit
method.