Circular dependency in Node.js and Nest.js
Tags: nestjs, circulardependency, node, forwardref
Published: 2023-04-10 11:56:55

---
title: Circular dependency in Node.js and Nest.js
published: true
description: Circular dependency in Node.js and Nest.js
tags: #nestjs #circularDependency #nodejs #forwardRef
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-04-10 11:33 +0000
---

Circular dependency is a situation that arises when modules depend directly or indirectly on one another in a program.

In Nest.js, with dependency injection (DI), it can be easily identified as the affected module would not be created during application startup until this dependency issue is fixed. However, if this is a Node.js circular dependency, typical of incomplete module loading somewhere else in the application not using DI, it may go unnoticed resulting in runtime errors.

In this article, we will create, discuss and fix circular dependency in Node.js and Nest.js.

## Circular dependency in Node.js

This is a case where 2 or more modules depend on each other by sharing program elements. For example, a module `A` imports from `B` and at same time, `B` imports from  `A`. 
We can summarise the dependency like so (`A` -> `B` -> `A`).

The problem here, based on how module loading works is that Node.js while loading `A` will find `B` and while loading `B`, will discover `B` requires again module `A`. In other to avoid an infinite loop, Node.js will return an incompletely loaded module `A` in `B` which will result in undefined imports in module `B`.

### The weird undefined
As pointed out earlier, incomplete module loading can result in unexpected errors during runtime as certain imports will be undefined. To drive this point, we will create a small Node example script to show how this can arise, how to troubleshoot it and refactor to verify our solution.

Consider a small Node.js project containing 3 files, `A.js`, `B.js` and `index.js` where `A.js` depends on `B.js` and vice versa.

Say the content of `A.js` is as follows:

```js
const { utilB } = require('./B')

const nameObject = {
    name: 'whatever'
}

const utilA = () => {
    console.log('inside A')
    console.log(`name is '${nameObject.name}' in A`)
    utilB()
}

module.exports = {
    utilA,
    nameObject
}

```

`B.js` is as follows:

```js
const { nameObject } = require('./A')

const utilB = () => {
    console.log('inside B')
    console.log(`name is '${nameObject.name}' in B`)
}

module.exports = {
    utilB,
}
```

`index.js` is as follows:

```js
const { utilA } = require('./A')

utilA()

```

As we can see, `A.js` depends on `B.js` and `B.js` depends on `A.js`. As a result of this, imports from `A.js` in `B.js` will be undefined as `A.js` is not fully loaded at this time.
Run `node index.js` in this project directory and observe that the output in the terminal throws an error `Cannot read properties of undefined` which we know for a fact is defined. This is simply because of an incompletely loaded module `A.js` in `B.js`.

```sh
profg@AwajimitopsMBP circular-node (main) $ node index.js
inside A
name is 'whatever' in A
inside B
/Users/profg/Desktop/github/circular-deps/circular-node/B.js:5
    console.log(`name is '${nameObject.name}' in B`)
                                    ^

TypeError: Cannot read properties of undefined (reading 'name')
    at utilB (/Users/profg/Desktop/github/circular-deps/circular-node/B.js:5:37)
    at utilA (/Users/profg/Desktop/github/circular-deps/circular-node/A.js:10:5)
    at Object.<anonymous> (/Users/profg/Desktop/github/circular-deps/circular-node/index.js:3:1)
    at Module._compile (node:internal/modules/cjs/loader:1105:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
    at node:internal/main/run_main_module:17:47
```

### Troubleshoot circular dependency with Madge

Identifying this in a real project may be hectic and that is where [madge](https://www.npmjs.com/package/madge) comes in.

Madge is a tool for identifying circular dependencies in Node.js projects. I use it in my Nest.js projects too.

To do this, we need to install it as a development dependency and create a script for it.

Add madge to the project
```sh 
npm install madge -D
```

My package.json looks like this now

```json
{
  "name": "circular-node",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "find-circular-deps": "madge . -c"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "madge": "^6.0.0"
  }
}

```
Now, check for circular dependencies with madge by running the script `find-circular-deps` in the package.json file. Note that `madge . -c` simply tells madge we are checking in this same directory. emphasis on the dot `.` used.

```sh
 npm run find-circular-deps
```

Notice it correctly detects we have a circular dependency.

![Found 1 circular dependency](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tcd2foyehykkanh4vo2p.png)


### Avoid circular dependency in Node.js by refactor
We will fix this issue by a simple refactor. The refactor will depend on your project. However, for this dummy example used above, we can solve this by combining `A.js` and `B.js` into one file or move their relationship to a new file from where they can independently import their dependency. I will go with the latter here for sake of demonstration. Any option works.

We will create a new module `C.js` and move into it the object `nameObject` and refactor `A.js` and `B.js` to use `C.js` thus, breaking the circular dependency.

`C.js` will look like so

```js
const nameObject = {
    name: 'whatever'
}


module.exports = {
    nameObject
}
```

`A.js` will now look like so

```js
const { utilB } = require('./B')
const { nameObject } = require('./C')

const utilA = () => {
    console.log('inside A')
    console.log(`name is '${nameObject.name}' in A`)
    utilB()
}

module.exports = {
    utilA,
}
```

`B.js` will now look like so

```js
const { nameObject } = require('./C')

const utilB = () => {
    console.log('inside B')
    console.log(`name is '${nameObject.name}' in B`)
}

module.exports = {
    utilB,
}
```

Run madge with `npm run find-circular-deps` and notice the circular dependency is now gone.

![Madge no circular dependency](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9c1bjc6psox8gt46svmf.png)

Run the script `index.js` with `node index.js` and notice it now runs as expected to completion with no errors.

![node index.js runs successfully](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/okwx6jign2um48j8tvb7.png)

## Circular dependency in Nest.js
[Circular dependency in Nest.js](https://docs.nestjs.com/fundamentals/circular-dependency#forward-reference) occurs when two classes depend on each other. This can be easily identified as Nest.js will throw an error and will only build after you resolve the circular dependency.

To show this, we need to quickly bootstrap a nest application and create 2 module classes that depend on each other. I will be leveraging the Nest.js cli tool to quickly bootstrap one and modify to achieve this.

Note that though we chose to demonstrate this with module classes, same can be applied in exactly same way with service/provider classes.


### The Nest.js circular dependency problem

For this example, we will consider 2 module classes with names `AppModule` and `FileModule` which depends on each other.

The `app.module.ts` module file

```ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { FileModule } from './file.module';

@Module({
  imports: [FileModule],
  controllers: [AppController],
  providers: [AppService],
  exports: [AppService],
})
export class AppModule {}

```

The `file.module.ts` module file

```ts
import { Module } from '@nestjs/common';
import { AppController as FileController } from './app.controller';
import { AppService } from './app.service';
import { FileService } from './file.service';
import { AppModule } from './app.module';

@Module({
  imports: [AppModule],
  controllers: [FileController],
  providers: [FileService],
})
export class FileModule {
  constructor(
    private readonly appService: AppService,
  ) {}

  onApplicationBootstrap() {
    console.log(this.appService.getHello());
  }
}

```
What we have done above is to forcefully create a circular dependency by importing also the AppModule in the FileModule just to access our appService. On attempt to start the app, you should get this error in the terminal.

![Circular dependency error in terminal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vebgdehzxs106uqykg8h.png)

### Fix Nest.js circular dependency by refactor

A refactor most times, is what we need to get around this issues. 

In other cases, one may need to create a new module to hold the shared dependencies and have that module imported in our 2 new modules to break the circular dependency but it depends on what one intends to achieve and the project architecture at hand. However, in this simple example, we created the problem and so it is easy to fix as we know all we need to do is not import the entire `AppModule` module and just inject our `appService` as a provider in `FileModule`.

Refactor `file.module.ts` like so

```ts
import { Module } from '@nestjs/common';
import { AppController as FileController } from './app.controller';
import { AppService } from './app.service';
import { FileService } from './file.service';

@Module({
  controllers: [FileController],
  providers: [FileService, AppService],
})
export class FileModule {
  constructor(
    private readonly appService: AppService,
  ) {}

  onApplicationBootstrap() {
    console.log(this.appService.getHello());
  }
}

```
![Refactor successful app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/owo3ja3gn3l03kc8swll.png)

### Fix Nest.js circular dependency with forwardRef

When a refactor is not an option for your use case, then Nest.js `forwardRef` utility function can be used to overcome this issue.

Back to our original failing `FileModule` version that imports the entire `AppModule`, we just need to use `forwardRef` with the import of `AppModule`.

The file `file.module.ts` will now look like so

```ts
import { Module, forwardRef } from '@nestjs/common';
import { AppController as FileController } from './app.controller';
import { AppService } from './app.service';
import { FileService } from './file.service';
import { AppModule } from './app.module';

@Module({
  imports: [forwardRef(() => AppModule)],
  controllers: [FileController],
  providers: [FileService],
})
export class FileModule {
  constructor(
    private readonly appService: AppService,
  ) {}

  onApplicationBootstrap() {
    console.log(this.appService.getHello());
  }
}
```

Run the app and notice it works fine.
![forwardRef successful app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5xsvssh2vlyx7w8yh8qg.png)


## Conclusion:

Circular dependency in Nest.js and Node.js are simply the same things. However, Nest.js detects this during dependency injection and prevents it by throwing a circular dependency error. The main problem is just an incompletely loaded module which can still happen in other parts of a Nest.js application not using dependency injection and go unnoticed until something begins to break.

We can find these issues using the npm package [madge](https://www.npmjs.com/package/madge) and fix them by a simple refactor. Where a refactor is not an option, we can always use the Nest.js utility function `forwardRef` which works for both modules and providers.

The code used in this article can be found on [github](https://github.com/successgilli/circular-deps). The initial circular dependency code problems are in the branch `circular-dependency-problem`