Circular dependency between modules
What a circular dependency is
A circular dependency happens when two (or more) modules or providers depend on each other, directly or through a chain.
For example:
AuthModule → UsersModule → AuthModuleor
AuthService → UsersService → AuthServiceAn example of the problem with modules
// auth.module.ts
@Module({
imports: [UsersModule], // AuthModule depends on UsersModule
providers: [AuthService],
})
export class AuthModule {}// users.module.ts
@Module({
imports: [AuthModule], // UsersModule depends on AuthModule
providers: [UsersService],
})
export class UsersModule {}On startup:
Error: Nest cannot create the module instance.
A circular dependency has been detected between modules.NestJS can't build the dependency graph, because each module is waiting for the other to be created.
Why this happens
Nest builds a Dependency Graph:
- When
AuthModuleimportsUsersModule, Nest first has to createUsersModule. - But
UsersModule, in turn, importsAuthModule, and the process loops.
As a result, NestJS doesn't know where to start, and throws an error about the circular dependency.
The same problem can happen with providers
@Injectable()
export class AuthService {
constructor(private readonly usersService: UsersService) {}
}
@Injectable()
export class UsersService {
constructor(private readonly authService: AuthService) {}
}Here NestJS also gets "stuck" creating the instances:
AuthServiceneedsUsersService,- but creating
UsersServiceneedsAuthService.
How to solve the problem
NestJS provides a special tool, forwardRef().
The fix for modules
Use forwardRef() when importing the dependent module:
// auth.module.ts
@Module({
imports: [forwardRef(() => UsersModule)], // the key change
providers: [AuthService],
exports: [AuthService],
})
export class AuthModule {}// users.module.ts
@Module({
imports: [forwardRef(() => AuthModule)], // the mutual dependency is now allowed
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}Now Nest knows these modules reference each other "lazily", and can build the dependency graph correctly.
The fix for services
Also with forwardRef():
@Injectable()
export class AuthService {
constructor(
@Inject(forwardRef(() => UsersService))
private readonly usersService: UsersService,
) {}
}
@Injectable()
export class UsersService {
constructor(
@Inject(forwardRef(() => AuthService))
private readonly authService: AuthService,
) {}
}forwardRef() tells the DI container:
"Don't create this dependency right now, create it once it's actually needed."
How this works internally
- With a normal dependency, Nest creates instances at initialization time.
- With
forwardRef(), it registers a lazy reference, and fills in the dependency later, once all the providers are available.
Design tips (to avoid cycles)
- Split responsibilities: if two modules depend on each other, the logic was probably split poorly.
→ Pull the shared part out into a third module (e.g.
SharedModuleorCoreModule). - Export only what's needed. Don't export entire modules unnecessarily, just the specific services.
- Avoid mutual dependencies between services. Sometimes a direct reference can be replaced with events, callbacks, or repositories.
What happens if you don't use forwardRef()
NestJS:
-
won't be able to resolve the dependencies;
-
will throw:
javascriptNest can't resolve dependencies of the AuthService (?). Please make sure that the argument UsersService at index [0] is available in the AuthModule context.
And the application simply won't start.
Summary
| Point | Description |
|---|---|
| What it is | A situation where two modules or services depend on each other |
| Cause | Nest can't determine the order to create the instances in |
| Fix | Use forwardRef(() => TargetModule) or @Inject(forwardRef(() => TargetService)) |
| Better approach | Rethink the architecture and move the shared logic into a separate module |
| If left unfixed | A startup error: "Circular dependency detected" |
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.