Express Server with Socket.IO
Published April 17, 2020 • Last reviewed April 17, 2020
Socket.io is a powerful library that helps enable two-way communications. Here's how I set up a project with socket.io and Express.
Socket.IO is a library that allows real time applications to be built. The communication between the client and the server is bi-directional. This allows the server to send updates to clients as needed. Common use cases for Socket.IO include messaging apps and multiplayer online games.
In this post, I want to talk about creating an Express server with Socket.IO. Specifically, we want to retain the ability for clients to make regular HTTP requests and handle them with Express.
Structure:
\server
constants.js
index.js
\socket
index.js
\routes
index.js
api-one.js
api-two.js
index.js is the main entry point for the server.
\socket\index.js contains logic for handling socket events received from clients.
\routes\api-one.js and \routes\api-two.js (as examples) contain endpoints.
index.js
const express = require("express");
const app = express();
// Configure middleware
// Initialize database connection
// More logic ...
const server = app.listen(PORT);
const io = require("socket.io")(server);
app.set("socketio", io);
require("./socket")(app, io);
app.use(require("./routes"));The main entry point includes logic for middlewares, database connections, and the socket initialization. I've found that keeping logic in the entry file is fine for the size of applications I am building.
We can set the io object to access it in our route handlers.
app.set("socket.io", io);\socket\index.js
const { actions } = require("../constants");
module.exports = (app, io) => {
io.sockets.on("connection", (socket) => {
// Handle events
socket.on("FROM_CLIENT", (action) => {
switch (action.type) {
case actions.JOIN_ROOM:
// Handle join room event
break;
case actions.MESSAGE:
// Handle message event
// Data can be access in action.payload
break;
// More actions
default:
console.log(`${action.type} is not handled.`);
}
});
});
};Only listening to one FROM_CLIENT event and switching over the action type keeps all of our socket logic consolidated into one block. All the logic for handling different action types is placed here. For organization, I define any action types in a separate constants.js file. This has the added benefit of making it very easy to ensure my client-side action types match up with my server-side action types.
// constants.js
module.exports = {
actions: {
JOIN_ROOM: "JOIN_ROOM",
MESSAGE: "MESSAGE",
// Other actions
},
// Other constants
};\routes\api-one.js
const express = require("express");
const router = express.Router();
router.get("/", (req, res) => {
// Handle get request
});
router.post("/", (req, res) => {
// Handle post request
// Access the io object
const io = req.app.get("socketio");
});Having access to the io object in our route handler means that the server can take responsibility for emitting events. This reduces the code that the client applications need to write. As well, if the route handler involves many steps before sending a response back to the client, we can emit events for each of those steps.
This setup has worked well for my personal projects so far but by no means is it the definitive way to setup an Express server with Socket.IO.
Connect
Last reviewed on February 20, 2026