How to organize socket-io files in an express server?

Viewed 198

I have recently started using socket-io for a live-chatting feature in a project of mine. I have everything working fine but as of now, I have all the server side socket-io stuff (connection, middleware, event handlers, etc.) in the main "index.js" file. It isn't a big deal now as I am only listening to a couple of events, but I would like to organize and separate the code into smaller files before it gets out of hand.

Here is an example of what the socket-io portion of the code looks like in my index.js file:

const express = require("express");
const app = express();
const http = require("http");
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);
const jwt = require("jsonwebtoken");

const activeSockets = {};

io.use((socket, next) => {
  const { token } = socket.handshake.auth;
  if (!token) return next(new Error("Invalid or missing token"));
  const { _id } = jwt.verify(token, process.env.JWT_KEY);
  socket.handshake.auth._id = _id;
  next();
});

const addSocket = (socket) => {
  const { _id } = socket.handshake.auth;
  if (!activeSockets[_id]) activeSockets[_id] = [socket.id];
  else activeSockets[_id] = [...activeSockets[_id], socket.id];
};

const removeSocket = (socket) => {
  const { _id } = socket.handshake.auth;
  if (!_id || !activeSockets[_id]) return;
  const index = activeSockets[_id].indexOf(socket.id);
  activeSockets[_id].splice(index, 1);
  if (activeSockets[_id].length < 1) delete activeSockets[_id];
};

io.on("connection", (socket) => {
  addSocket(socket);
  socket.on("typing", (isTyping, recipients, conversation, sender) => {
    recipients.forEach((recipient) => {
      if (activeSockets[recipient._id]) {
        activeSockets[recipient._id].forEach((r) => {
          socket.to(r).emit("typing", isTyping, conversation, sender);
        });
      }
    });
  });
  socket.on("sendMessage", ({ message, recipients, conversation }) => {
    recipients.forEach((recipient) => {
      if (activeSockets[recipient._id]) {
        activeSockets[recipient._id].forEach((r) => {
          socket.to(r).emit("receiveMessage", { message, conversation });
        });
      }
    });
  });
  socket.on("disconnect", () => {
    removeSocket(socket);
  });
});


const port = process.env.PORT || 5000;
server.listen(port, () => {
  console.log("Listening: ", port);
});

I'm just struggling to find an efficient way to extract the socket-io code into smaller more organized pieces. Should the only thing in index.js related to socket-io be the connection itself? And then I have files for different event handlers that take an "io" parameter and then I call "io.on(...)" in those external functions? Or perhaps should I listen for all the events in index.js and then extract only the logic of each event into separate files? Something like:

io.on("eventName", someExternalFunction)

This is my first experience with socket-io so I'm not too sure of the "best practices".

Thank you to anyone who can offer help!

1 Answers

You could put the socket event handlers into modules like.

chat/connection.js:

module.exports = (io) => {
  io.on("connection", (socket) => {
    console.log('connection was made');
  });
}

Then in index.js require('./chat/connection.js')(io);

Related