0

I have a app.js

const express = require('express');
const app = express();
const server = require('./server.js');

// app.use
const io = require('socket.io').listen(server);
io.on('connection', function (socket) {
   ...
});

module.exports = app;

And a server.js

const app = require('./app');
const server = app.listen(5000 || process.env.PORT, () => {
    console.log('App listening on port 5000!');
})
module.exports = server;

If I put the server in a separated file the socket is not working, but if I start the server inside the app.js the socket works. What I'm doing wrong?

Tomasz Mularczyk
  • 34,501
  • 19
  • 112
  • 166
Mattia
  • 130
  • 2
  • 11
  • the two files are requiring each other and you are also not exporting the `io` instance – messerbill Feb 02 '18 at 17:23
  • Why do I need to export the `io` instance? I'm trying to do some test like point 3 [here](http://www.albertgao.xyz/2017/05/24/how-to-test-expressjs-with-jest-and-supertest/). And I need the two files separated. – Mattia Feb 02 '18 at 17:34

1 Answers1

1

The issue here is that you have a circular dependency where app.js is loading server.js and server.js is loading app.js. You can't do that for this type of code.

It has an issue because you're trying to load server.js from within app.js and then in the process of loading server.js, it attempts to load app.js and get its exports, but app.js hasn't finished loading yet and thus hasn't even returned its exports yet. So, the loader either thinks there are no exports or recognizes the circular request (I'm not sure which), but in either case the exports from app.js don't work because of the circular requires.

There are several different ways to solve this. The two most common ways are:

  1. Break some code into a common third module that each of these can load and only have one of these load the other.

  2. Rather than having server.js load app to get the app object, have app.js pass the app object to server.js in a constructor function rather than trying to execute at module load time.

Here's how the constructor function idea would work:

app.js

const express = require('express');
const app = express();

// load server.js and call it's constructor, passing the app object
// that module constructor function will return the server object
const server = require('./server.js')(app);

// app.use
const io = require('socket.io').listen(server);
io.on('connection', function (socket) {
   ...
});

module.exports = app;

server.js

// export constructor function that must be called to initialize this module
module.exports = function(app) {

    const server = app.listen(5000 || process.env.PORT, () => {
        console.log('App listening on port 5000!');
    });
    return server;
};

So, rather than server.js trying to load the app.js module to get the app object, the app object is "pushed" to it with a constructor function. This prevents the circular dependency.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Thanks, can you explain better _pass the app object to server.js in a constructor function rather than trying to execute at module load time._ or give me a link where is explained? – Mattia Feb 02 '18 at 17:52
  • Yes thanks very clear, I'm trying if everything works before closing the question. Where can I learn more about this constructor technique? Is new to me. – Mattia Feb 02 '18 at 18:23
  • @Mattia - It's really just exporting a function that can be called after the module is loaded. It just allows you to pass some shared variables to the module initialization code which you move inside of this function. The technique is often called a "module constructor", though it's really just exporting a function designed to be called right after loading the module. I'm not aware of any particular reference on the topic. – jfriend00 Feb 02 '18 at 18:33
  • @Mattia - Here's one other answer on the topic: [Node.js - use of module.exports as a constructor](https://stackoverflow.com/questions/38511976/how-can-i-export-socket-io-into-other-modules-in-nodejs/38514118#38514118). – jfriend00 Feb 02 '18 at 18:33
  • so is the module.exports exporting the object `server` and not the function? – Mattia Feb 02 '18 at 18:48
  • @Mattia - The `server.js` exports are exporting a function. That's all. The line `const server = require('./server.js')(app);` first does the `require('./server.js')`. That gets the function that it exported. Then, it calls that function with the `(app)` and calling that function returns the `server` object. This could have been written on two separate lines to make it more obvious: `let startServer = require('./server.js');` followed by `let server = startServer(app)`. But, I used a common shorthand to combine them into one line. – jfriend00 Feb 02 '18 at 18:51
  • Now is clear. The server is working, thank you for the help – Mattia Feb 02 '18 at 19:15