151

I have a folder, that has index.js and a couple of models (classes) index.js

module.exports = {
   Book : require('./book'),
   Author : require('./author')
}

book.js

var Author = require('./author')
var Book = models.ActiveRecord.extend({
    schema : {
        belongsTo : {
            author : Author
        }
    }
})
module.exports = Book

author.js

var Book = require('./book')
var Author = models.ActiveRecord.extend({
    schema : {
        hasMany : {
            author : Book
        }
    }
})

module.exports = Author

The problem is that Author class does not seem to find the Book! It's just an empty Object. However, if I switch the exports in index.js, putting Book after Author - it works, but then the other model stops working.

I don't want to do any hacks to make it work.

Community
  • 1
  • 1
user3677173
  • 2,559
  • 2
  • 17
  • 16

1 Answers1

250

This is because you have a circular dependency. Node.js handles this in a very specific way:

  1. The first module loads and runs (in this case, book.js). It (book.js) will load and run the second module (author.js) when it (book.js) requires the other (author.js)

  2. When the second module (author.js) is loaded and run, it (author.js) requires the first module (book.js) but it (author.js) will receive a partially filled object - however many things were set on the exports in book.js before it required author.js will be in that object

  3. After book.js is completely run through, the object author.js got from require('./book') will be the full book.js module object

For more info, here's the docs: http://nodejs.org/api/modules.html

If its possible to dynamically add that schema to one of those ActiveRecord objects, that's one way to solve this. This is actually kind of a tricky situation. In fact, even without the module system, this would cause problems for you. If you put all this code in one file, how would you make it work?

B T
  • 57,525
  • 34
  • 189
  • 207
  • 1
    I know you shouldn't put everything in one file, but I'm saying just experiment like that. My point there is that it isn't node.js's module system that is causing the problem here, it is the code itself. – B T May 26 '14 at 17:54
  • And as a matter of fact, Java has the same issues with circuclar dependencies - its a problem any language can have. If all you need is a reference to you circular dependency partner, javascript can do it just as easily as java. However, here, you are trying to use the object before its been built (in the extend) – B T May 26 '14 at 17:55
  • Yeah, actually now i understood it. Never came to mind. So, there is only one way - to add it dynamically, i guess. Or passing a model name as a string. Thanks for your response! – user3677173 May 26 '14 at 17:57
  • 27
    I highly recommend [Madge](https://www.npmjs.com/package/madge) for debugging this sorts of issues in more complex cases (ie with more files). Once installed you could use `madge -c ` to detect any circular dependencies. – schu34 Mar 21 '18 at 14:38
  • 12
    Thanks. Pretty painful that it doesn't even throw an error but just returns an empty object. – Robert Oschler Nov 18 '18 at 18:54
  • @RobertOschler It actually does that to help more elegantly deal with circular dependencies. That behavior means that any module that builds its exports without executing code from any circular dependencies can work fine despite the circular dependency. – B T Nov 19 '18 at 07:47
  • @schu34 thank you for mentioning Madge — a simple tool but very powerful for debugging. – ariestav May 14 '19 at 01:09