dols3m
dols3m

Reputation: 1975

Nest.js: Circular dependencies in dynamic modules

What is the correct way to import dynamic modules when there's a circular dependency between them? I simply changed forwardRef(() => MyModule) to forwardRef(() => MyModule.forRoot()), and I'm getting Nest can't resolve dependencies error.

Upvotes: 15

Views: 20274

Answers (3)

Zeeshan Ahmad Khalil
Zeeshan Ahmad Khalil

Reputation: 861

I also got this error and I know that I can solve this issue using forwardRef() but I didn't know where exactly I can put forwardRef().

Actually, the nestjs error shows where I have to put forwardRef(). Let's have a look at the following error:

Error: Nest cannot create the AuthModule instance.
The module at index [3] of the AuthModule "imports" array is undefined.

My auth.module.ts

@Module({
  imports: [
    OccasionModule,
    forwardRef(() => OrderModule),
    BalanceModule,
    UserModule,
    PassportModule,
    ReferralModule,
    RoleModule,
    JwtModule.register({
      secret: jwtSecret,
    }),
    MongooseModule.forFeature([{
      name: OTP.name,
      schema: OTPSchema
    }]),
  ],
  providers: [
    AuthService,
    LocalStrategy,
  ],
  controllers: [AuthController],
  exports: [
    AuthService
  ],
})

In my case module at index [3] of the AuthModule "imports" array is UserModule so I have to use forwardRef() there and my auth.module.ts will become:

@Module({
  imports: [
    OccasionModule,
    forwardRef(() => OrderModule),
    BalanceModule,
    forwardRef(() => UserModule),
    PassportModule,
    ReferralModule,
    RoleModule,
    JwtModule.register({
      secret: jwtSecret,
    }),
    MongooseModule.forFeature([{
      name: OTP.name,
      schema: OTPSchema
    }]),
  ],
  providers: [
    AuthService,
    LocalStrategy,
  ],
  controllers: [AuthController],
  exports: [
    AuthService
  ],
})

export class AuthModule { }

Also don't forget to change

private userService:
            UserService,

to

@Inject(forwardRef(() =>
            UserService
        ))
        private userService:
            UserService,

in the auth.service.ts.

After doing this you might get another dependency issue but with a different [index] of another Module. If you keep repeating the above process to add forwardRef() then you will eventually solve the circular dependency issue.

But it's not good to have too many circular dependencies. It's better to design modules of your project in such a way that you get minimum possible or no circular dependency.

Upvotes: 0

tito.300
tito.300

Reputation: 1136

My issue was a silly one.

I had an extra comma in the Providers array of the module which I assume caused Nest to try and resolve and nonexisting provider. Removing the comma fixed it.

enter image description here

Upvotes: 3

Anees Hameed
Anees Hameed

Reputation: 6554

The best way to deal with nest module dependency is to import dependencies always through modules and never through services. If you run into a circular dependency, nest will show you an error and you can easily fix it with forwardRef().

let say your application has 3 tightly coupled modules.

  • @moduleA()
  • @moduleB()
  • @moduleC()

And two supporting modules

  • @moduleX()
  • @moduleY()
  • @moduleZ()

Also, all modules are exporting services with the same name.

Consider a situation where

@moduleA() imports [serviceB, serviceX]

And

@moduleB() imports [serviceA, serviceY, serviceZ]

Now in @moduleC() if you want to use serivceA() then you will have to

@moduleC() imports [serviceA, serviceB, serviceX, serviceY, serviceZ]

In most cases, nest throws the correct error saying which dependency is missing where. But some times, nest only says dependency at [index] is missing, nest doesn't says where it is missing. This will lead to great level of confusion.

A neat approach is to always import modules

@moduleA() imports [moduleB, moduleX]

@moduleB() imports [moduleA, moduleY, moduleZ]

@moduleC() imports [moduleA]


If nest is complaining again about dependency issue then import modules using forwardRef

@module({
     imports: [
       forwardRef(() => ModuleA)
       ModuleB
       ],
    controllers: [],
    providers: []
})

Sometimes even after doing all these, you may again run into dependency not available error. Then you can use ModuleRef. Let's take the same example where you want to use ServiceA() in ModuleC()

You will then be adding serviceA() to providers[] in ModuleC() and in the serviceC() class you need to add the following.

import { Injectable, OnModuleInit } from '@nestjs/common'; \\need OnModuleInit 
import { ServiceA} from '../FolderA/serviceA.service';

export class ServiceC implements OnModuleInit {
   
   private serviceA: ServiceA;
   constructor(private readonly moduleRef: ModuleRef) {}

   onModuleInit() {
        this.serviceA= this.moduleRef.get(ServiceA);
    }
   
   foo(){
     console.log(this.serviceA.someFunction())
   }
}

Please check more at Official documentation.

Upvotes: 25

Related Questions