Circular required modules in node
I noticed the classic “undefined is not a function” error occuring in suprising places. Here is an example:
|
|
The undefined function is forms.getForm
. This was odd because 1. forms
clearly contained that function, and 2. That code passed my test suite. The “undefined is not a function” error was only happening when using the app.
As it turns out, the problem was a circular dependency. responses
requires forms
, but forms
also requires responses
. I had no idea you couldn’t do this. I agreed that it was a bit of a code smell to have a circular dependency like that, but it could be done right? Well, no.
The node docs explain this briefly, and explain that in order to prevent an infinite loop, an unfinished copy of the exported object can be returned. This is what I experienced. The forms
variable was an empty object.
Solutions
Move the require statement to where it’s needed
The easiest solution is to move the require
statement into the function that uses it. The above code would become:
|
|
To me that looks really ugly. What if you make a lot of use of that module? You’d have to include it in each function. Also, I like glancing at the top of a file and knowing what modules the file depends upon. This is possibly inefficient as well.
Or, change your architecture
Just remove the circular logic. Simple right? Here’s how I did it.
This clearly wasn’t going to fly. I decided to move my “formatting” logic out of responses. This makes sense. But here’s what I ended up with:
Still circular. One feature in particular connected the reponses to the formatter. What if I pull that out?
So now I have a bigger circle. That’s all I’ve been doing. I decided I needed to be orchestraing these modules’ connections from an outside place. The router made sense.
The two colors represent two different routes. It’s only doing one thing from each of the modules, but still, I don’t like having large routers.
I could make a new “manager” type module. But I’ve found that “manager” type structures just make your code that much harder to reason about. So I decided to leave it as is.
In summary, circular dependency is bad in node. Keep that in mind as you design your architecture.