Circular Dependencies
A circular dependency occurs when two classes depend on each other. For example, class A needs class B, and class B also needs class A.
Overview
Circular dependencies can arise in NestJS between Modules (Module A imports Module B, and Module B imports Module A) and between Providers/Services (Service A injects Service B, and Service B injects Service A).
When this happens, the NestJS IoC container cannot resolve the dependency graph because it gets stuck in an infinite loop trying to instantiate them. It will throw a fatal error during application bootstrap.
While the best solution is to refactor your code to avoid the circular dependency entirely, NestJS provides the forwardRef() utility function to resolve them when they are unavoidable.
Key Concepts
forwardRef(): A utility function that allows Nest to reference classes which aren’t yet defined.@Inject(forwardRef(() => Class)): How you apply the forward reference in a constructor.- Module Circularity: Forward references must be applied on both sides of the circular dependency.
Code Examples
Resolving Service Circularity
Let’s say CatsService and CommonService depend on each other.
// cats.service.ts
import { Injectable, Inject, forwardRef } from '@nestjs/common';
import { CommonService } from './common.service';
@Injectable()
export class CatsService {
constructor(
// Wrap the token in forwardRef()
@Inject(forwardRef(() => CommonService))
private commonService: CommonService,
) {}
}
// common.service.ts
import { Injectable, Inject, forwardRef } from '@nestjs/common';
import { CatsService } from './cats.service';
@Injectable()
export class CommonService {
constructor(
// Must also wrap the token on the other side!
@Inject(forwardRef(() => CatsService))
private catsService: CatsService,
) {}
}
Resolving Module Circularity
The same concept applies if CatsModule and CommonModule import each other.
// cats.module.ts
import { Module, forwardRef } from '@nestjs/common';
import { CommonModule } from './common.module';
@Module({
// Use forwardRef in the imports array
imports: [forwardRef(() => CommonModule)],
})
export class CatsModule {}
// common.module.ts
import { Module, forwardRef } from '@nestjs/common';
import { CatsModule } from './cats.module';
@Module({
// Must be used on both sides
imports: [forwardRef(() => CatsModule)],
})
export class CommonModule {}
Best Practices
- Refactor First: A circular dependency is almost always a sign of a design flaw. Before using
forwardRef(), ask yourself: “Can I extract the shared logic into a third service/module that both of these depend on?” (e.g., extracting to aSharedUtilityService). - ModuleRef: If you have deep circular dependencies that
forwardRef()struggles with, you can use theModuleRefclass to manually retrieve the dependency later in the lifecycle (e.g., inside anonModuleInithook) rather than in the constructor.