0

Desired Behaviour

The socket.io initialisation docs provides this example, which I currently use in app.js:

import { createServer } from "http";
import { Server } from "socket.io";

const httpServer = createServer();
const io = new Server(httpServer, {
  // ...
});

io.on("connection", (socket) => {
  // ...
});

httpServer.listen(3000);

I also have three objects before the io block that need to be referenced from within the block:

var logged_in_users = {};
var users_in_rooms = {};
var live_edits_occurring = {}; 

As you can see, everything happens in app.js.

I want to refactor app.js so that:

  1. All logic in the io.on('connection') function block is outside of app.js
  2. The global objects (logged_in_users etc) are accessible in middleware files

Actual Behaviour

I am getting this error:

ReferenceError: logged_in_users is not defined

which means I cannot access logged_in_users from a middleware file.

Question

How can I get the objects like logged_in_users to be globally accessible?

So that they can be referenced and updated by all the middleware files?

Would app.locals be a good approach?

// app.js  
app.locals.logged_in_users = {};
app.locals.users_in_rooms = {};
app.locals.live_edits_occurring = {}; 

I haven't used app.locals before and I wouldn't know how to access app within the middleware.

I also don't know if you can update app.locals from middleware, which I would need to do.

What I've Tried

Following the docs on application structure, I have:

app.js

const express = require('express');
const { createServer } = require('http');
const { Server } = require('socket.io');
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer);


const all_socketio_logic = require('./middleware/socketio/all_socketio_logic').all_socketio_logic;    
  
const on_connection = async (socket) => {
  all_socketio_logic(io, socket);
};

io.on('connection', on_connection);

/middleware/socketio/all_socketio_logic.js

import { heres_my_details } from './event_handler_heres_my_details';

const all_socketio_logic = (io, socket) => {

  // i want these available in the middleware defined below     
  var logged_in_users = {};
  var users_in_rooms = {};
  var live_edits_occurring = {};

  // sanity checks - all returning values - GOOD   
  console.log('socket.id is: ' + socket.id);
  console.log('logged_in_users is: ');
  console.log(logged_in_users);
  console.log('users_in_rooms is: ');
  console.log(users_in_rooms);
  console.log('live_edits_occurring is: ');
  console.log(live_edits_occurring);

  // this is being returned to browser - GOOD 
  socket.emit('heres_your_socket_id', socket.id);  

  // this middleware is running - GOOD  
  socket.on('heres_my_details', heres_my_details);

  /*

  i will add other event handlers here:  
  
  socket.on('another_event_01', another_event_01);
  socket.on('another_event_02', another_event_02);

  etc  

  */

};

export { all_socketio_logic };

/middleware/socketio/event_handler_heres_my_details.js

const heres_my_details = (data) => {
  
  console.log('heres_my_details:');
  console.log(data);
  
  var username = data.username;
  
  // if client is logged in
  if (username !== null) {
    var user_email = data.user_email;
  
    /* 
      if the logged_in_users object doesn't already contain 
      the clients socket id (as a property), add the socket 
      id property and define object username and user_email 
    */
  
    if (!logged_in_users.hasOwnProperty(socket.id)) {
      // the error is here - cannot access logged_in_users  
      logged_in_users[socket.id] = {};   
      logged_in_users[socket.id].username = username;
      logged_in_users[socket.id].user_email = user_email;
      console.log('logged_in_users AFTER ADDING new object:');
      console.log(logged_in_users);
    }
  }
};

export { heres_my_details };

I get the following logged - GOOD:

socket.id is: zhWMY0EpkNAWURN3AAAB
logged_in_users is:
{}
users_in_rooms is:
{}
live_edits_occurring is:
{}
heres_my_details:
{
  username: 'my username',
  user_email: 'my@email_address.com',
  logged_in: true
}

Error from event_handler_heres_my_details.js - BAD:

ReferenceError: logged_in_users is not defined

Similar questions (with outdated or incomplete answers)

Use socket.io in expressjs routes instead of in main server.js file

Node.js how to use socket.io in express route

Using socket.io with express 4 generator

NodeJS socket.io in a router page

Separating socket.io logic from app.js in feathersjs

How to move Socket.io io.on('connection') code another js file?

Separate Socket.io into app.js

Separating file server and socket.io logic in node.js

Environment

node v16.13.0
express: ^4.17.1
socket.io: ^4.3.2
socket.io-client: ^4.3.2

user1063287
  • 10,265
  • 25
  • 122
  • 218

1 Answers1

0

One way to do this:

appp.js

const express = require('express');
const { createServer } = require('http');
const app = express();
const httpServer = createServer(app);

const io = require('./ws.js')(httpServer);

ws.js

module.exports = function ws(httpServer) {

    var logged_in_users = {};
    var users_in_rooms = {};
    var live_edits_occurring = {}; 

    const { Server } = require('socket.io');
    const io = new Server(httpServer);

    httpServer.use(function(req, res, next){
        // attach io, etc. to every req object
        req.ws = {
            io,
            logged_in_users,
            users_in_rooms,
            live_edits_occurring
        };
        // call next to pass req to handler
        next();
    });
    
    io.on('connection', on_connection);

};
Molda
  • 5,619
  • 2
  • 23
  • 39
  • I'm curious. What version of Node are you using that allows you to pass additional arguments to `require('path', ...)`? Because I'm pretty sure `const io = require('./ws.js', httpServer);` isn't doing what you hope it will. ;) – Rob Raisch Dec 01 '21 at 21:22
  • 1
    @RobRaisch sure, my bad, it should be `const io = require('./ws.js')(httpServer);` Thanks for pointing that out. – Molda Dec 02 '21 at 10:06