AboutBlogContact
Backend EngineeringSeptember 15, 2016 5 min read 191Updated: June 22, 2026

How to Implement Real-Time Features with WebSockets and Socket.io 1.x (2016)

AunimedaAunimeda
📋 Table of Contents

Short answer: npm install socket.io, attach to your HTTP server with io = require('socket.io')(server), emit events with socket.emit('event', data), listen on the client with socket.on('event', callback). For authentication: verify JWT in the Socket.io middleware before connection completes. For multiple servers: add socket.io-redis adapter.


Server Setup

// server.js - Node.js 6 + Express + Socket.io 1.x
var express   = require('express');
var http      = require('http');
var socketio  = require('socket.io');
var jwt       = require('jsonwebtoken');

var app    = express();
var server = http.createServer(app);
var io     = socketio(server);

var JWT_SECRET = process.env.JWT_SECRET;

// Authentication middleware for Socket.io
io.use(function(socket, next) {
    var token = socket.handshake.query.token ||
                socket.handshake.headers['authorization'];

    if (!token) {
        return next(new Error('Authentication required'));
    }

    // Remove "Bearer " prefix if present
    if (token.startsWith('Bearer ')) {
        token = token.slice(7);
    }

    jwt.verify(token, JWT_SECRET, function(err, decoded) {
        if (err) {
            return next(new Error('Invalid token'));
        }
        socket.userId   = decoded.userId;
        socket.userRole = decoded.role;
        next();
    });
});

// Connection handler
io.on('connection', function(socket) {
    console.log('User ' + socket.userId + ' connected (' + socket.id + ')');

    // Auto-join user's private room
    socket.join('user:' + socket.userId);

    // Admins join admin room
    if (socket.userRole === 'admin' || socket.userRole === 'operator') {
        socket.join('operators');
    }

    // Listen for client joining specific order room
    socket.on('watch:order', function(orderId) {
        // Verify user owns this order or is admin
        OrderRepository.isOwnerOrAdmin(orderId, socket.userId, socket.userRole)
            .then(function(allowed) {
                if (allowed) {
                    socket.join('order:' + orderId);
                    socket.emit('watching', { orderId: orderId });
                }
            });
    });

    socket.on('disconnect', function() {
        console.log('User ' + socket.userId + ' disconnected');
    });
});

server.listen(3001, function() {
    console.log('WebSocket server on port 3001');
});

module.exports = { io };

Emitting Events from Business Logic

// orderService.js - emit events when orders change
var io = require('./server').io;

function updateOrderStatus(orderId, newStatus, updatedBy) {
    return OrderRepository.updateStatus(orderId, newStatus)
        .then(function(order) {
            // Notify the customer
            io.to('user:' + order.userId).emit('order:updated', {
                orderId: orderId,
                status:  newStatus,
                updatedAt: new Date().toISOString(),
            });

            // Notify all operators
            io.to('operators').emit('order:status_changed', {
                orderId:    orderId,
                status:     newStatus,
                customerId: order.userId,
                updatedBy:  updatedBy,
            });

            // Notify anyone watching this specific order
            io.to('order:' + orderId).emit('order:updated', {
                orderId:   orderId,
                status:    newStatus,
                updatedAt: new Date().toISOString(),
            });

            return order;
        });
}

Client-Side (Browser)

<!-- index.html -->
<script src="/socket.io/socket.io.js"></script>
<script>
var token = localStorage.getItem('access_token');

var socket = io('https://api.myapp.com', {
    query:     { token: token },
    transports: ['websocket', 'polling'],
    // Try WebSocket first, fall back to long-polling
    reconnection:      true,
    reconnectionDelay: 1000,
    reconnectionAttempts: 5,
});

socket.on('connect', function() {
    console.log('Connected to WebSocket server');

    // Watch current order
    var orderId = document.getElementById('order-id').value;
    socket.emit('watch:order', parseInt(orderId));
});

socket.on('order:updated', function(data) {
    // Update UI when order status changes
    document.getElementById('order-status').textContent = data.status;
    showNotification('Order ' + data.orderId + ' is now ' + data.status);
});

socket.on('connect_error', function(error) {
    console.error('Connection error:', error.message);
    // Fall back to polling the API
    startPolling();
});

socket.on('disconnect', function(reason) {
    console.log('Disconnected:', reason);
    if (reason === 'io server disconnect') {
        // Server forced disconnect (e.g., token expired)
        socket.connect();
    }
});
</script>

Client-Side (React/React Native 2016)

// services/socket.js
import io from 'socket.io-client';
import store from '../store';

var socket = null;

export function connect(token) {
    if (socket && socket.connected) return socket;

    socket = io('https://api.myapp.com', {
        query: { token },
        transports: ['websocket'],
    });

    socket.on('connect', () => {
        store.dispatch({ type: 'SOCKET_CONNECTED' });
    });

    socket.on('order:updated', (data) => {
        store.dispatch({ type: 'ORDER_STATUS_UPDATED', payload: data });
    });

    socket.on('disconnect', () => {
        store.dispatch({ type: 'SOCKET_DISCONNECTED' });
    });

    return socket;
}

export function disconnect() {
    if (socket) {
        socket.disconnect();
        socket = null;
    }
}

export function watchOrder(orderId) {
    if (socket && socket.connected) {
        socket.emit('watch:order', orderId);
    }
}

Scaling: Redis Adapter for Multiple Servers

// When running multiple Node.js servers behind a load balancer,
// each server has its own in-memory socket connections.
// socket.io-redis broadcasts events across all servers.

var redis    = require('socket.io-redis');
var socketio = require('socket.io');

var io = socketio(server);
io.adapter(redis({ host: 'localhost', port: 6379 }));

// Now: io.to('user:42').emit('event', data)
// Redis pub/sub delivers to ALL servers → ALL sockets for user 42

Connection Limit Management

// Track connected users per server
var connectedUsers = new Map();  // userId → socketId

io.on('connection', function(socket) {
    // Enforce one connection per user (optional)
    var existing = connectedUsers.get(socket.userId);
    if (existing) {
        io.to(existing).emit('force_disconnect', { reason: 'new_login' });
    }
    connectedUsers.set(socket.userId, socket.id);

    socket.on('disconnect', function() {
        if (connectedUsers.get(socket.userId) === socket.id) {
            connectedUsers.delete(socket.userId);
        }
    });
});

// Monitor connections
setInterval(function() {
    var rooms     = io.sockets.adapter.rooms;
    var operators = rooms['operators'] ? rooms['operators'].length : 0;
    console.log('Connections:', io.engine.clientsCount, '| Operators online:', operators);
}, 30000);

Nginx WebSocket Proxy

# WebSocket requires specific nginx headers
location /socket.io/ {
    proxy_pass         http://localhost:3001;
    proxy_http_version 1.1;
    proxy_set_header   Upgrade    $http_upgrade;
    proxy_set_header   Connection "upgrade";
    proxy_set_header   Host       $host;
    proxy_read_timeout 600s;   # Keep-alive for WebSocket connections
    proxy_send_timeout 600s;
}

Dashboard Results (2016)

Operator dashboard before WebSockets: refreshed every 30 seconds via AJAX polling. After:

AJAX Polling WebSockets
Update latency 0-30 seconds < 200ms
Server load (50 ops) 50 × 2 req/min = 100 req/min 0 requests (push)
React time to status change 30s avg 0.2s avg

Operators could now respond to new orders in seconds instead of minutes. The average time-to-first-contact with customers dropped from 4.2 minutes to 38 seconds.


Aunimeda builds production-grade backend systems - APIs, microservices, real-time applications, and system integrations.

Contact us for backend engineering services. See also: Custom Software Development, Web Development

Read Also

Node.js + TypeScript: Building a Production REST API from Scratch in 2026aunimeda
Backend Engineering

Node.js + TypeScript: Building a Production REST API from Scratch in 2026

A complete guide to building a production-ready REST API with Node.js and TypeScript - authentication, validation, error handling, rate limiting, logging, and deployment. No shortcuts.

Clean Architecture in Node.js: A Practical Guide Without the Academic Fluffaunimeda
Backend Engineering

Clean Architecture in Node.js: A Practical Guide Without the Academic Fluff

Clean Architecture sounds great in theory. In practice, most implementations add complexity without benefit. This guide shows the pattern that actually works in Node.js - dependency inversion, use cases, and repository pattern with real, runnable code.

Redis Data Structures in Production: Beyond SET and GETaunimeda
Backend Engineering

Redis Data Structures in Production: Beyond SET and GET

Most teams use Redis as a glorified hash map. This guide covers the data structures that solve real production problems - sorted sets for leaderboards, streams for durable event queues, HyperLogLog for UV counting at scale, and Lua scripts for atomic operations you can't otherwise do safely.

Need IT development for your business?

We build websites, mobile apps and AI solutions. Free consultation.

Get Consultation All articles