Skip to content

Websocket Express

DodaTech 5 min read

title: "WebSocket with Express.js" description: "Learn how to integrate WebSocket with Express.js applications for combining REST APIs with real-time WebSocket communication." weight: 15 date: 2026-06-28 lastmod: 2026-06-28 tags: ["apis", "websocket"]


Express.js can serve both HTTP REST endpoints and WebSocket connections on the same server. This lesson covers integrating the `ws` library with Express for hybrid HTTP/WebSocket applications.

## What You'll Learn

- Setting up WebSocket with Express
- Sharing Express middleware with WebSocket
- HTTP-to-WebSocket upgrade routing
- Hybrid REST + WebSocket architecture
- Authentication integration

## Why It Matters

Most web applications need both REST APIs and real-time features. Running both on the same server simplifies deployment, shares authentication logic, and reduces operational complexity.

## Real-World Use

A project management app uses Express for REST endpoints (CRUD for projects, tasks) and WebSocket for real-time collaboration (live cursor updates, drag-and-drop sync, notification delivery).

## Flow Chart

```mermaid
flowchart LR
    A[Client] --> B[Express Server]
    B --> C{Route}
    C -->|/api/*| D[REST Handler]
    C -->|/ws| E[WebSocket Upgrade]
    D --> F[JSON Response]
    E --> G[WebSocket Connection]
    G <--> H[Real-time Messages]

Code Examples

Example 1: Express with WebSocket Server

const express = require('express');
const http = require('http');
const WebSocket = require('ws');

const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });

// Express routes
app.get('/api/status', (req, res) => {
  res.json({ status: 'running', clients: wss.clients.size });
});

// WebSocket connections
wss.on('connection', (ws, req) => {
  console.log('WebSocket client connected');
  
  ws.on('message', (message) => {
    const data = JSON.parse(message);
    
    // Broadcast to all clients
    wss.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(JSON.stringify(data));
      }
    });
  });
  
  ws.send(JSON.stringify({ type: 'connected', message: 'Welcome' }));
});

server.listen(3000, () => {
  console.log('Server on http://localhost:3000');
  console.log('WebSocket on ws://localhost:3000/ws');
});

Expected output: Express serves REST on port 3000, and WebSocket accepts connections on the same port.

Example 2: Route-Based WebSocket Upgrade

const express = require('express');
const http = require('http');
const WebSocket = require('ws');

const app = express();
const server = http.createServer(app);

// Separate WebSocket servers for different paths
const chatWss = new WebSocket.Server({ noServer: true });
const notificationWss = new WebSocket.Server({ noServer: true });

chatWss.on('connection', (ws) => {
  ws.on('message', (msg) => console.log('Chat:', msg.toString()));
  ws.send('Connected to chat');
});

notificationWss.on('connection', (ws) => {
  ws.send('Connected to notifications');
});

server.on('upgrade', (request, socket, head) => {
  const pathname = new URL(request.url, 'http://localhost').pathname;
  
  if (pathname === '/ws/chat') {
    chatWss.handleUpgrade(request, socket, head, (ws) => {
      chatWss.emit('connection', ws, request);
    });
  } else if (pathname === '/ws/notifications') {
    notificationWss.handleUpgrade(request, socket, head, (ws) => {
      notificationWss.emit('connection', ws, request);
    });
  } else {
    socket.destroy();
  }
});

app.get('/api/info', (req, res) => {
  res.json({
    chatConnected: chatWss.clients.size,
    notificationConnected: notificationWss.clients.size,
  });
});

server.listen(3000);

Expected output: Different WebSocket endpoints for chat and notifications, routed by URL path.

Example 3: Express Middleware with WebSocket Auth

const express = require('express');
const WebSocket = require('ws');
const cookieParser = require('cookie-parser');

const app = express();
app.use(cookieParser());
app.use(express.json());

// Express authentication middleware
app.use('/api', (req, res, next) => {
  const token = req.cookies.token || req.headers.authorization;
  if (!token) return res.status(401).json({ error: 'Unauthorized' });
  req.user = verifyToken(token);
  next();
});

const server = http.createServer(app);
const wss = new WebSocket.Server({ noServer: true });

wss.on('connection', (ws, req) => {
  console.log(`User ${req.user.id} connected via WebSocket`);
  ws.send(JSON.stringify({ type: 'auth', status: 'connected' }));
});

server.on('upgrade', (request, socket, head) => {
  const token = parseCookies(request.headers.cookie).token 
    || request.headers.authorization;
  
  if (!token) {
    socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
    socket.destroy();
    return;
  }

  const user = verifyToken(token);
  if (!user) {
    socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
    socket.destroy();
    return;
  }

  request.user = user;
  wss.handleUpgrade(request, socket, head, (ws) => {
    wss.emit('connection', ws, request);
  });
});

Expected output: WebSocket connections require valid authentication tokens, with unauthorized connections rejected during upgrade.

Common Mistakes

Mistake Explanation
Not sharing the HTTP server Both Express and WebSocket must share the same http.createServer instance
Missing upgrade handler Without the upgrade event handler, WebSocket connections fail on custom paths
Blocking the event loop with WebSocket handlers WebSocket message handlers should be async and non-blocking
Forgetting to handle server errors Add error handlers for both Express and WebSocket server errors
Mixing ws and wss on same port A port cannot serve both HTTP and HTTPS; choose one scheme for the server

Practice Questions

  1. How do you run WebSocket and Express on the same port?
  2. How do you route WebSocket connections to different handlers based on URL?
  3. How do you share authentication between Express and WebSocket?
  4. What is the noServer option in WebSocket.Server?
  5. How do you broadcast messages to all connected WebSocket clients?

Challenge

Build a hybrid application with Express REST endpoints for user management and WebSocket for real-time notifications. Users authenticate via REST, receive a JWT token, and use that token to authenticate their WebSocket connection.

FAQ

Can I use Socket.IO with Express?

Yes, Socket.IO works seamlessly with Express. It uses the same HTTP server and provides additional features like rooms and auto-reconnection.

How do I scale Express + WebSocket across multiple servers?

Use a Redis adapter (for Socket.IO) or a message broker to synchronize WebSocket state across Express instances.

Do Express middleware functions work with WebSocket?

Express middleware only works with HTTP requests. For WebSocket authentication, handle it in the upgrade handler or the connection event.

What happens when Express route conflicts with WebSocket path?

WebSocket upgrade requests are intercepted before Express routing. If the path matches a WebSocket handler, Express routes are bypassed.

Can I use WebSocket with Express Router?

Yes, but the upgrade handling works at the server level, not the router level. Route WebSocket paths manually in the upgrade handler.

How do I handle CORS for WebSocket on Express?

WebSocket CORS is handled by the Origin header during the handshake. Validate the Origin in the upgrade handler.

Mini Project

Build a collaborative task management app with Express REST API and WebSocket real-time updates. Users can create, update, and delete tasks via REST, and all changes are broadcast to connected clients via WebSocket. Include authentication for both HTTP and WebSocket.

What's Next

Learn about Socket.IO for enhanced WebSocket functionality

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro