Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deal with cyclic dependencies in Node.js

People also ask

How does node handle circular dependency?

A circular dependency between Node. js modules occurs when the modules require() each other. This dependency results in an incomplete module being loaded, and mysterious problems later. Using Madge is an excellent way to detect the dependencies.

How would you fix a cyclic dependency?

A cyclic dependency is an indication of a design or modeling problem in your software. Although you can construct your object graph by using property injection, you will ignore the root cause and add another problem: property injection causes Temporal Coupling. Instead, the solution is to look at the design closely.

How do I get around circular dependency?

Circular dependencies can be introduced when implementing callback functionality. This can be avoided by applying design patterns like the observer pattern.

How do I manage dependencies in node JS?

Option 2: Specify a new dependency in package. Another way to install a new package is to specify it as a dependency in package. json , then run npm install with no arguments. The new dependency and all of its dependencies will be installed.


Try to set properties on module.exports, instead of replacing it completely. E.g., module.exports.instance = new ClassA() in a.js, module.exports.ClassB = ClassB in b.js. When you make circular module dependencies, the requiring module will get a reference to an incomplete module.exports from the required module, which you can add other properties latter on, but when you set the entire module.exports, you actually create a new object which the requiring module has no way to access.


While node.js does allow circular require dependencies, as you've found it can be pretty messy and you're probably better off restructuring your code to not need it. Maybe create a third class that uses the other two to accomplish what you need.


[EDIT] it's not 2015 and most libraries (i.e. express) have made updates with better patterns so circular dependencies are no longer necessary. I recommend simply not using them.


I know I'm digging up an old answer here... The issue here is that module.exports is defined after you require ClassB. (which JohnnyHK's link shows) Circular dependencies work great in Node, they're just defined synchronously. When used properly, they actually solve a lot of common node issues (like accessing express.js app from other files)

Just make sure your necessary exports are defined before you require a file with a circular dependency.

This will break:

var ClassA = function(){};
var ClassB = require('classB'); //will require ClassA, which has no exports yet

module.exports = ClassA;

This will work:

var ClassA = module.exports = function(){};
var ClassB = require('classB');

I use this pattern all the time for accessing the express.js app in other files:

var express = require('express');
var app = module.exports = express();
// load in other dependencies, which can now require this file and use app

Sometimes it is really artificial to introduce a third class (as JohnnyHK advises), so in addition to Ianzz: If you do want to replace the module.exports, for example if you're creating a class (like the b.js file in the above example), this is possible as well, just make sure that in the file that is starting the circular require, the 'module.exports = ...' statement happens before the require statement.

a.js (the main file run with node)

var ClassB = require("./b");

var ClassA = function() {
    this.thing = new ClassB();
    this.property = 5;
}

var a = new ClassA();

module.exports = a;

b.js

var ClassB = function() {
}

ClassB.prototype.doSomethingLater() {
    util.log(a.property);
}

module.exports = ClassB;

var a = require("./a"); // <------ this is the only necessary change

The solution is to 'forward declare' your exports object before requiring any other controller. So if you structure all your modules like this and you won't run into any issues like that:

// Module exports forward declaration:
module.exports = {

};

// Controllers:
var other_module = require('./other_module');

// Functions:
var foo = function () {

};

// Module exports injects:
module.exports.foo = foo;