Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I use a global variable to share socket.io instance across entire server

The following is my server.js file in my node.js application. I want my socket.io instance to be accessed by other files on my server in order to emit events from my API (listingRoutesApi, userRoutesApi etc.) (refer to code).

The problem I have is that my routes are declared before the server is created; however, the socket.io instance is created after the server is created.

The solution I've used is to declare a global io variable that will allow me to emit events from anywhere within my web app like so:

global.io.of('/analytics').to(listing._id).emit('message', "There was a post.");

My question is: are there any pitfalls / dangers from doing this and will I encounter any scalability issues in the long-term? Additionally, is there a better way of achieving my objective?

Code within my server.js file:

const app = express();    

app.use('/api', listingRoutesApi);
app.use('/api', userRoutesApi);
app.use('/api', imageRoutesApi);
// ...plenty more endpoints here...

app.use(serveStatic(path.join(__dirname, "/dist")));
app.use(history());
app.use(serveStatic(path.join(__dirname, "/dist")));

const server = app.listen(port, () => { console.log('server started ' + port); });

/* Start socket. */
global.io = socketio(server);

const analytics = global.io.of("/analytics");

analytics.on('connection', (socket) => {
    socket.on('join', (data) => {
        socket.join(data.room);
        analytics.in(data.room).emit('message', `New user joined ${data.room}`);
    });
    socket.on('leave', (data) => {
        analytics.in(data.room).emit('message', `User leaving ${data.room}`);
        socket.leave(data.room);
    });
    socket.on('disconnect', () => {
        console.log('user disconnected');
    });
});

I'm asking this question since this SO post answers a similar question by declaring a getIOInstance function and passes it to all modules that require it. While, it works, it doesn't feel very elegant and seems a little unnecessary given I expect to only ever have exactly one socket.io instance in my application.

Additionally, I would assume the challenge I'm having is a very common one; however, I haven't been able to find many solutions to address it and none that suggest using a global variable.

like image 442
p4t Avatar asked Oct 28 '18 10:10

p4t


1 Answers

Node.js is a modular environment. Modules are suppose to address some flaws that globals have.

Modules naturally provide singleton instances, in case there's a need to have only one instance:

app.js

module.export = express();

server.js

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

// can go to app.js if configured but unlistened app is needed for reusability or testing
app.use(/* router */);

module.export = app.listen(...);

socketio.js

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

module.export = socketio(server);

index.js

const app = require('./app');
const io = require('./socketio');
...

Express also provides a container for application global dependencies, application settings table. It can be used everywhere application instance is available, e.g. as req.app.get(...) inside middlewares. Accessing Express application instance outside middlewares won't be a problem if it's a singleton as well:

app.js

module.export = express();

index.js

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

app.use(/* router */);
...

const server = app.listen(...);
const io = socketio(server);

app.set('io', io);

// available as req.app.get('io') inside middlewares
// and as require('./app').get('io') outside them
like image 140
Estus Flask Avatar answered Nov 15 '22 07:11

Estus Flask