module.exports gets undefined while importing on another file

Shankar Regmi picture Shankar Regmi · Mar 27, 2018 · Viewed 7.8k times · Source

This is my file structure for simple Express app.

server/
  |- models
  |--- users.js
  |- index.js

index.js is the entry point

const app = require('express')();
const Server = require('http').createServer(app)
const io = require('socket.io')(Server);
......
.....
.....
.....
....
module.exports = {
    Server,
    io
}

inside my models/users.js I need the io variable exported above just to emit some event to all connected clients if new user is added. for this I imported

const { io } = require('../index');

but io is undefined. I have tried few other similar ways

const io = require('../index').io // 

and

const io = require('../').io /index is useless on require

but none worked. Am I being silly or did I miss something here. any help would be highly appreciated.

Thanks

Answer

UberMario picture UberMario · Mar 27, 2018

This is an issue with:

Importing in the right order

SlaWitDev was right about the line const socketIOEvents = require('./socketIOEvents'); being a part of the issue.

This is a file that instantiates users.js. On instantiation of users.js, you are including the file you are in the process of instantiating. In other words, we haven't hit the bottom of the lines of code in index.js, for it to export the io object to that file.

index.js -> socketIOEvents/index.js -> models/index.js, -> models/users.js

Checking the order

I've added a line of code to the top of these files to log when they get called, and then a line just before module.exports in the index.js file:

[nodemon] starting `node server`
index.js
socketIOEvents/index.js
models/index.js
models/users.js
Initializing routes
index.js module.exports assignment
Server is running on port 9000.

As you can see, user.js gets called before we export modules.

Checking the imported object

As a result of the previous problem, if we add this line in users.js:

const test = require('../index');
console.log('test', test);

we get the output:

test {}

This means the object we imported doesn't have any keys, so performing object destructuring where you pull out the io key in:

const { io } = require('../index');

won't work.

A solution:

If we move the module.exports lines and const io line in index.js to above socketIOEvents, you'll see that it starts to work:

...
const config = require('./config');

// start socket.io
const io = require('socket.io')(Server);

module.exports = {
  Server,
  io
};

const socketIOEvents = require('./socketIOEvents');
...

Note this is likely an antipattern and not a final solution, as module.exports is usually at the end of the file.

Alternate solutions:

  • Pass io into your model in socketIOEvents, the same way you pass it into your socketIOEvents file, by passing it as the argument into the function.

  • Instance io in a separate file with Server being passed into it. Use this reference in both the main index.js file and the users.js file