The short answer
You probably have a circular dependency, i.e. you have a file a that requires file b, but file b also requires file a. In combination with using module.exports = something (instead of modifying the existing exports object), this leads to the situation where one of the modules will get an empty object returned from requireing the other one (none of its exports seem to exist there).
The quick fix is most likely to replace module.exports = { app, db } with exports.app = app; exports.db = db or Object.assign(exports, { app, db }).
As to why, and what other options exist in case that is not the best solution, read below.
The long answer
It sounds as if you have a circular dependency problem.
Let me explain, and bear with me, we'll eventually get to your problem.
See, every module starts out with an empty object as module.exports (also available as exports). If you only modify this object by doing exports.something = something (or module.exports.something = something), things can work in many cases even with a circular dependency at the top level, as long as the properties of the exports object are accessed only later on. For example:
/* a.js */
const b = require('./b')
exports.getX = function () {
return b.getY() * 2
}
exports.getZ = function () {
return 5
}
/* b.js */
const a = require('./a')
exports.getY = function () {
return a.getZ() * 3
}
/* main.js */
const a = require('./a')
console.log(a.getX()) // Returns 30
Here we have a circular dependency (a depends on b, but b also depends on a). Yet, it works.
The above code works because when a.js is evaluated, its exports object will already exist, and when it then requires b.js and b.js requires a.js again, then even though a.js has not even finished running its top-level code yet, the a variable in b.js can already get assigned the exports object of a.js. It is an empty object at that point, but by the time the line return a.getZ() * 3 runs, the object will have the property getZ set.
main requires a
a requires b
b requires a and gets an object that is currently empty
b defines getY on its exports and returns
a defines getX and getZ on its exports (which is the exact same object that b already got a reference to!) and returns
main calls a.getX()
a.getX calls b.getY
b.getY only now accesses the property getZ of a which does exist by now (it was set in step 5).
Note that it would not have worked if we had written const { getZ } = require('./a') because then the getZ property would have been accessed already at step 3 when it wouldn't have existed yet.
But, similarly, it also would have stopped working if we had written a.js like this:
const b = require('./b')
function getX () {
return b.getY() * 2
}
function getZ () {
return 5
}
module.exports = { getX, getZ }
The big difference here is that we are now reassigning the exports object! So, the object that b gets at step 3 is the original (empty) object, but we are then reassigning it to a new object (instead of modifying the existing object), and the code in b never has a chance to get a reference to that object! Then, you have this exact situation: A require gives you an empty object, and trying to call a.getZ will fail with a.getZ is not a function (because it is undefined).
(By the way, module.exports = something is not the same as exports = something, because the latter reassigns the local exports variable and does not change what other modules will see as exports from yours!)
My assumption right now is that you have a similar problem here - that the file that does the DB connection is requiring the file that uses the DB connection, but the other way round as well.
You now have two options:
- Replace
module.exports = { app, db } with exports.app = app; exports.db = db or Object.assign(exports, { app, db }) - both of which mutate the existing exports object instead of replacing it.
- Require one of the files at a later point in time, either by requiring it inside of another function or by creating a placeholder variable
let otherModule and exporting an init method that actually fills it using otherModule = require('./otherModule') and then calling this init method in a second step after requiring, thereby decoupling the evaluating of the module code and the requiring of its dependencies, breaking the circular dependency.
The first option is probably the easiest, most immediate solution to the problem. But, I don't know the rest of your code, so there may be other obstacles that prevent this solution from being viable.
So, let me explain the second option in detail, because it can solve the issue often too.
Example of the second option:
/* a.js */
let b
function getX () {
return b.getY() * 2
}
function getZ () {
return 5
}
function init () {
b = require('./b')
}
module.exports = { init, getX, getZ }
/* b.js */
const a = require('./a')
function getY = function () {
return a.getZ() * 3
}
module.exports = { getY }
/* main.js */
const a = require('./a')
a.init()
console.log(a.getX()) // Returns 30
This code now works even though it reassigns module.exports, because now things happen in a different order:
main requires a
a replaces its exports object with one that contains init, getX and getZ and returns [note that this part of the code previously ran much later]
main calls a.init()
a.init requires b
b requires a and gets its final exports object with all the methods [note that previously it got a not-yet-filled object because a was also not fully loaded yet!]
b replaces its exports object with one that contains getY and returns
a.init returns
main calls a.getX()
- Everything works from now on because both
a and b by now have references to each others' fully-filled exports objects already
The other way to implement the second option of "requiring at a later point in time" is to require the other module inside an exported function that is already used, but this can have its downsides (such as duplicate code if it is needed in many functions, and slightly slower execution because require has to be called over and over again). In our example, it would mean doing this:
/* b.js */
function getY () {
const a = require('./a')
return a.getZ() * 3
}
module.exports = { getY }