Socket.io attaches to the same HTTP server that Express already uses โ they share port 5000 in development without any conflict. The Socket.io server wraps the HTTP server, intercepts WebSocket upgrade requests, and handles socket lifecycle events alongside Express’s normal route handling. In this lesson you will install Socket.io, attach it to the Express HTTP server, configure CORS for the socket connection, emit your first test events, and verify the real-time channel is working before adding any application-specific logic.
Installation
cd server
npm install socket.io
cd ../client
npm install socket.io-client
Attaching Socket.io to Express
// server/index.js โ modified to support Socket.io
require('dotenv').config();
const express = require('express');
const http = require('http'); // โ needed to share server with Socket.io
const { Server } = require('socket.io');
const cors = require('cors');
const connectDB = require('./src/config/db');
const app = express();
const server = http.createServer(app); // wrap Express app in HTTP server
// โโ Socket.io attached to the same HTTP server โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const io = new Server(server, {
cors: {
origin: process.env.CLIENT_URL || 'http://localhost:5173',
methods: ['GET', 'POST'],
credentials: true,
},
pingTimeout: 60000, // disconnect after 60s without ping
pingInterval: 25000, // ping every 25s to keep connection alive
});
// โโ Express middleware โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
app.use(cors({ origin: process.env.CLIENT_URL, credentials: true }));
app.use(express.json());
// โโ Express routes โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
app.use('/api/auth', require('./src/routes/auth'));
app.use('/api/posts', require('./src/routes/posts'));
// โโ Socket.io connection handler โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
io.on('connection', (socket) => {
console.log(`Socket connected: ${socket.id}`);
socket.on('disconnect', (reason) => {
console.log(`Socket disconnected: ${socket.id} โ ${reason}`);
});
socket.on('error', (err) => {
console.error(`Socket error on ${socket.id}:`, err.message);
});
});
// โโ Start server โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const PORT = process.env.PORT || 5000;
const start = async () => {
await connectDB();
server.listen(PORT, () => console.log(`Server + Socket.io โ http://localhost:${PORT}`));
};
start().catch(err => { console.error(err); process.exit(1); });
// โโ Export io for use in route handlers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
module.exports = { app, io };
app with http.createServer(app) and calling server.listen(PORT) instead of app.listen(PORT). Socket.io needs access to the raw HTTP server (not the Express app) to intercept the WebSocket upgrade request. Express and Socket.io share the same port with no conflict โ HTTP requests go to Express, WebSocket upgrade requests go to Socket.io.io instance from index.js so you can use it inside route handlers and controllers to emit events when data changes. When a user posts a comment via POST /api/posts/:id/comments, the comment controller can import io and emit a new-comment event to the relevant room immediately. This connects your REST API mutations to real-time notifications.cors option in the new Server(server, { cors: {...} }) call, matching the same origin you set for Express.Verifying the Connection with a Browser Test
// Quick browser console test โ paste in DevTools console
// (while Socket.io client script is loaded or from the React app)
const socket = io('http://localhost:5000');
socket.on('connect', () => console.log('Connected! ID:', socket.id));
socket.on('disconnect', () => console.log('Disconnected'));
socket.on('error', (err) => console.error('Socket error:', err));
// Emit a test event
socket.emit('ping', { message: 'hello from browser' });
// Server console should log: Socket connected: abc123xyz
Accessing io in Controllers
// server/src/config/socket.js โ central socket access
let io;
const initSocket = (serverIo) => {
io = serverIo;
};
const getIO = () => {
if (!io) throw new Error('Socket.io not initialised');
return io;
};
module.exports = { initSocket, getIO };
// server/index.js
const { initSocket } = require('./src/config/socket');
const socketIo = new Server(server, { cors: { ... } });
initSocket(socketIo); // store reference
// server/src/controllers/commentController.js
const { getIO } = require('../config/socket');
const createComment = asyncHandler(async (req, res) => {
const comment = await Comment.create({ ... });
// Emit to all clients viewing this post
getIO()
.to(`post:${req.params.postId}`)
.emit('new-comment', comment);
res.status(201).json({ success: true, data: comment });
});
Socket.io Server-Side API โ Key Methods
| Method | Target | Example |
|---|---|---|
socket.emit(event, data) |
This socket only | Respond to the sender |
socket.broadcast.emit(event, data) |
All sockets except this one | Notify others of a user joining |
io.emit(event, data) |
All connected sockets | Server-wide announcement |
io.to(room).emit(event, data) |
All sockets in a room | Post-specific event |
socket.join(room) |
โ | Subscribe this socket to a room |
socket.leave(room) |
โ | Unsubscribe from a room |
socket.to(room).emit(event, data) |
Room except this socket | Notify room without echo |
Common Mistakes
Mistake 1 โ Calling app.listen() instead of server.listen()
โ Wrong โ Socket.io attached to the HTTP server but Express listens separately:
const server = http.createServer(app);
const io = new Server(server, { ... });
app.listen(5000); // wrong! Socket.io is on server, not app
โ Correct โ listen on the HTTP server:
server.listen(5000); // โ both Express and Socket.io share port 5000
Mistake 2 โ Missing Socket.io CORS configuration
โ Wrong โ Express CORS configured but Socket.io CORS not set:
app.use(cors({ origin: 'http://localhost:5173' })); // Express only
const io = new Server(server); // no Socket.io CORS โ browser blocked!
โ Correct โ configure CORS on the Socket.io server too:
const io = new Server(server, {
cors: { origin: 'http://localhost:5173', credentials: true },
}); // โ
Mistake 3 โ Using io.emit() for post-specific events
โ Wrong โ broadcasting a comment to ALL connected clients:
io.emit('new-comment', comment); // all 1000 connected users get this!
โ Correct โ emit only to the relevant room:
io.to(`post:${postId}`).emit('new-comment', comment); // โ only post viewers
Quick Reference
| Task | Code |
|---|---|
| Wrap Express in HTTP server | const server = http.createServer(app) |
| Create Socket.io server | const io = new Server(server, { cors: {...} }) |
| Listen for connections | io.on('connection', socket => { ... }) |
| Listen on same port | server.listen(PORT) |
| Emit to room | io.to('post:id').emit('event', data) |
| Emit to all | io.emit('event', data) |