How to avoid circular dependencies in NestJS
Tags: webdev
Published: 2022-07-07 16:35:07

**Written by [Samuel Olusola](https://blog.logrocket.com/author/samuelolusola/)✏️**

## Introduction

One of the beauties of NestJS is that it allows us to separate concerns within our applications. The NestJS architecture favors creating the logic layer as dependencies (i.e., services) that can be consumed by the access layers (i.e., controllers). 

Even though NestJS has a built-in dependency injection system that takes care of resolving dependencies needed in different parts of our code, care must still be taken when working with dependencies. One of the common problems encountered in this regard is circular dependencies. NestJS code will not even compile if there is an unresolved circular dependency. 

In this article, we will be learning about circular dependencies in NestJS, why they arise, and how we can avoid them. Rather than just presenting the workaround NestJS provides, I will be walking us through how to avoid circular dependencies by rethinking how we couple dependencies, which will be helpful for better architecture in our backend code. 

Avoiding circular dependencies also ensures that our code is easier to understand and modify, because a circular dependency means there is tight coupling in our code.

### Contents

*   [What is a circular dependency?](#what-is-circular-dependency)
*   [The NestJS dependency injection system](#nestjs-dependency-injection-system)
*   [How circular dependency issues arise](#how-circular-dependency-issues-arise)
*   [Avoiding circular dependencies by refactoring](#avoiding-circular-dependencies-refactoring)
*   [Working around circular dependencies with forward references](#working-around-circular-dependencies-forward-references)

## What is a circular dependency? <a name="what-is-circular-dependency">

In programming, a circular dependency occurs when two or more modules (or classes), directly or indirectly depend on one other. Say `A`, `B`, `C` and `D` are four modules, an example of direct circular dependency is `A`→`B`→`A`. Module `A` depends on module `B` which in turn depends on `A`. 

An example of indirect circular dependency is `A` → `B` → `C` → `A`. Module `A` depends on `B` which doesn’t depend on `A` directly, but later on in its dependency chain references `A`. 

Note that the concept of circular dependency is not unique to NestJS, and in fact, the modules used here as example don’t even have to be NestJS modules. They simply represent the general idea of modules in programming, which refers to how we organize code. 

Before we talk about circular dependencies (and how to avoid them) in NestJS, let us first discuss how the NestJS dependency injection system works. 

Knowing how NestJS handles dependency injection will make it easier to understand how a circular reference can occur within our dependencies and why NestJS compile can’t compile until the circular reference is resolved.

## The NestJS dependency injection system <a name="nestjs-dependency-injection-system">

In NestJS, with dependency injection (DI) we can delegate instantiation of dependencies to the runtime system, instead of doing it imperatively in our own code. For example, say we have a `UserService` defined as follows:

```javascript
import { Injectable } from '@nestjs/common';

@Injectable()
export class UserService {
  constructor() {}
  public async getUserById(userId: string) {
    ...
  }
  ...
}
```

Now, say we use the `UserService` as follows in the `UserController` class:

```javascript
import { Controller } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}

  public async getUserById(userId: string) {
    return await this.userService.getUserById(userId);
  }
}
```

In this case, `UserController` is an enquirer asking for the `UserService` as one of its dependencies. The NestJS dependency injector will check for the requested dependency in a container, where it stores references to providers defined in the NestJS project. 

The `@Injectable()` decorator that was used in the `UserService` definition marks the class as a provider that should be injectable by the NestJS dependency injection system, i.e., it should be managed by the container. When the TypeScript code is compiled by the compiler, this decorator emits metadata that the NestJS uses to manage dependency injection. 

![dependency injection Nestjs visualization](https://blog.logrocket.com/wp-content/uploads/2022/06/dependency-injection-Nestjs-visualization.png) 

In NestJS, each module has its own injector that can access the container. When you declare a module, you have to specify the providers that should be available to the module, except in cases where the provider is a global provider. 

For example, the `UserModule` is defined as follows:

```javascript
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';

@Module({
  providers: [UserService],
  controllers: [UserController],
  exports: [UserService],
})
export class UserModule {}
```

In a normal mode of execution, when an enquirer asks for a dependency, the injector checks the container to see if an object of that dependency has been cached previously. If so, that object is returned to the enquirer. Otherwise, NestJS instantiates a new object of the dependency, caches it, and then returns the object to the enquirer. 

The declaration `providers: [UserService]` is actually a shorthand for the following:

```javascript
providers: [
    {
      provide: UserService,
      useClass: UserService,
    },
]
```

The value of `provide` is an injection token that is used to identify the provider when it’s been enquired.

## How circular dependency issues arise <a name="how-circular-dependency-issues-arise">

The NestJS DI system relies heavily on the metadata emitted by the TypeScript compiler, so when there is a circular reference between two modules or two providers, the compiler won’t be able to compile any of them without further help. 

For example, say we have a `FileService` that we are using to manage files uploaded to our application, defined as follows:

```javascript
import { Injectable } from '@nestjs/common';
import { UserService } from '../user/user.service';
import { File } from './interfaces/file.interface';

@Injectable()
export class FileService {
  constructor(private readonly userService: UserService) {}
  public getById(pictureId: string): File {
    // not real implementation
    return {
      id: pictureId,
      url: 'https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50',
    };
  }

  public async getUserProfilePicture(userId: string): Promise<File> {
    const user = await this.userService.getUserById(userId);
    return this.getById(user.profilePictureId);
  }
}
```

The service has a `getUserProfilePicture` method that gets the image file attached as a user profile picture. The `FileService` needs the `UserService` injected as a dependency to be able to fetch a user. 

The `UserService` is also updated as follows:

```javascript
import { Injectable } from '@nestjs/common';
import { FileService } from '../file-service/file.service';

@Injectable()
export class UserService {
  constructor(private readonly fileService: FileService) {}
  public async getUserById(userId: string) {
    // actual work of retrieving user
    return {
      id: userId,
      name: 'Sam',
      profilePictureId: 'kdkf43',
    };
  }

  public async addUserProfilePicture(userId: string, pictureId: string) {
    const picture = await this.fileService.getById(pictureId);
    // update user with the picture url
    return { id: userId, name: 'Sam', profilePictureId: picture.id };
  }
}
```

We have a circular dependency in this case, as both `UserService` and `FileService` depend on each other (`UserService` → `FileService` → `UserService`). ![circular dependency visualization](https://blog.logrocket.com/wp-content/uploads/2022/06/circular-dependency-visualization.png) 

With the circular reference in place, the code will fail to compile.

## Avoiding circular dependencies by refactoring <a name="avoiding-circular-dependencies-refactoring">

The NestJS documentation advises that circular dependencies be avoided where possible. 

Circular dependencies create tight couplings between the classes or modules involved, which means both classes or modules have to be recompiled every time either of them is changed. As I mentioned in a [previous article](https://blog.logrocket.com/understanding-dependency-inversion-principle-typescript/), tight coupling is against the SOLID principles and we should endeavor to avoid it. 

We can remove the circular dependency easily in this example. The circular reference we have can also be represented as the following:

```javascript
UserService → FileService
and 
FileService → UserService
```

To break the cycle, we can extract the common features from both services into a new service that depends on both services. In this case, we can have a `ProfilePictureService` that depends on both `UserService` and `FileService`. 

The `ProfilePictureService` will have its own module defined as follows:

```javascript
import { Module } from '@nestjs/common';
import { FileModule } from '../file-service/file.module';
import { UserModule } from '../user/user.module';
import { ProfilePictureService } from './profile-picture.service';
@Module({
  imports: [FileModule, UserModule],
  providers: [ProfilePictureService],
})
export class ProfilePictureModule {}
```

Note that this module imports both the `FileModule` and the `UserModule`. Both imported modules have to export the services we want to use in `ProfilePictureService`. 

The `ProfilePictureService` will be defined as follows:

```javascript
import { Injectable } from '@nestjs/common';
import { File } from '../file-service/interfaces/file.interface';
import { FileService } from '../file-service/file.service';
import { UserService } from '../user/user.service';

@Injectable()
export class ProfilePictureService {
  constructor(
    private readonly fileService: FileService,
    private readonly userService: UserService,
  ) {}

  public async addUserProfilePicture(userId: string, pictureId: string) {
    const picture = await this.fileService.getById(pictureId);
    // update user with the picture url
    return { id: userId, name: 'Sam', profilePictureId: picture.id };
  }

  public async getUserProfilePicture(userId: string): Promise<File> {
    const user = await this.userService.getUserById(userId);
    return this.fileService.getById(user.profilePictureId);
  }
}
```

`ProfilePictureService` requires both `UserService` and `FileService` as its dependencies, and contains methods performing the actions we were previously doing in both `UserService` and `FileService`. 

The `UserService` doesn’t need to depend on `FileService` anymore, as you can see here:

```javascript
import { Injectable } from '@nestjs/common';

@Injectable()
export class UserService {
  public async getUserById(userId: string) {
    // actual work of retrieving user
    return {
      id: userId,
      name: 'Sam',
      profilePictureId: 'kdkf43',
    };
  }
}
```

Similarly, `FileService` doesn’t need to know a thing about `UserService`:

```javascript
import { Injectable } from '@nestjs/common';
import { File } from './interfaces/file.interface';

@Injectable()
export class FileService {
  public getById(pictureId: string): File {
    return {
      id: pictureId,
      url: 'https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50',
    };
  }
}
```

The relationship between the three services can now be represented as follows: ![refactoring visualization](https://blog.logrocket.com/wp-content/uploads/2022/06/refactoring-visualization.png) As you can see from the diagram, there is no circular reference among the services. 

Although this example on refactoring is about circular dependency between providers, one can use the same idea to avoid circular dependency among modules.

## Working around circular dependencies with forward references <a name="working-around-circular-dependencies-forward-references">

Ideally, circular dependencies should be avoided, but in cases where that’s not possible, Nest provides a way to work around them. 

A forward reference allows Nest to reference classes that have not yet been defined by using the `forwardRef()` utility function. We have to use this function on both sides of the circular reference. 

For example, we could have modified the `UserService` as follows:

```javascript
import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { FileService } from '../file-service/file.service';

@Injectable()
export class UserService {
  constructor(
    @Inject(forwardRef(() => FileService))
    private readonly fileService: FileService,
  ) {}

  public async getUserById(userId: string) {
    ...
  }
  public async addFile(userId: string, pictureId: string) {
    const picture = await this.fileService.getById(pictureId);
    // update user with the picture url
    return { id: userId, name: 'Sam', profilePictureUrl: picture.url };
  }
}
```

And then the `FileService` like so:

```javascript
import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { UserService } from '../user/user.service';
import { File } from './interfaces/file.interface';

@Injectable()
export class FileService {
  constructor(
    @Inject(forwardRef(() => UserService))
    private readonly userService: UserService,
  ) {}

  public getById(pictureId: string): File {
    ...
  }

  public async getUserProfilePicture(userId: string): Promise<File> {
    const user = await this.userService.getUserById(userId);
    return this.getById(user.id);
  }
}
```

With this forward reference, the code will compile without errors.

### Forward references for modules

The `forwardRef()` utility function can also be used to resolve circular dependencies between modules, but it must be used on both sides of the modules’ association. For example, one could do the following on one side of a circular module reference:

```javascript
@Module({
  imports: [forwardRef(() => SecondCircularModule)],
})
export class FirstCircularModule {}
```

## Conclusion

In this article, we learned what circular dependencies are, how dependency injection works in NestJS, and how issues of circular dependencies can arise. 

We also learned how we can avoid circular dependencies in NestJS and why we should always try to avoid it. Hopefully, you now know how to work around it in case it can’t be avoided using forward references. 

The code examples for this article are hosted [here](https://github.com/Olusamimaths/circular-dependency-article-project) on GitHub; there are three branches in the repository named `circular`, `fix/forward-referencing`, and `fix/refactoring`. You can use the branches to navigate to the different stages of the project.

---

##200’s only :heavy_check_mark: Monitor failed and slow network requests in production

Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, [try LogRocket.](https://logrocket.com/signup/)

[![LogRocket Sign Up](https://blog.logrocket.com/wp-content/uploads/2019/12/network-request-filter-2-1.png)](https://logrocket.com/signup/)

[LogRocket](https://logrocket.com/signup/) is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.