Managing WebSocket Connections in Next.js API Routes

Managing WebSocket Connections in Next.js API Routes

Introduction:

WebSockets are essential for real-time applications like chat apps, live updates, and collaborative tools. However, managing WebSocket connections in Next.js API routes can be challenging, especially after deployments or server restarts.

In this blog, we’ll explore the common issues with WebSocket connections in Next.js, understand why they occur, and discuss a practical solution for managing WebSocket servers effectively.

Uncaught TypeError: Cannot Read Property ‘X’ of Undefined

The Problem:

When implementing WebSocket connections in Next.js API routes, you might face issues like:

  1. Dropped Connections: WebSocket connections break after redeploying or restarting the server.
  2. Multiple Connections: Creating multiple WebSocket servers unintentionally when API routes are re-invoked.
  3. State Loss: Losing connection state after server-side changes.

// /pages/api/socket.js
import { Server } from ‘ws’;

export default function handler(req, res) {
const wss = new Server({ noServer: true });

wss.on(‘connection’, (ws) => {
ws.on(‘message’, (message) => {
console.log(‘Received:’, message);
});

ws.send(‘Connection established’);
});

res.end();
}

When deploying, you might notice:

  • Connections drop unexpectedly.
  • The WebSocket server is recreated multiple times.
  • Errors such as EADDRINUSE (Address in use).

Understanding the Issue

Next.js API routes are invoked on every request, meaning a new instance of the WebSocket server is created each time the API route is accessed. This behavior conflicts with WebSocket’s requirement for a single persistent server.

Additionally, serverless environments used in platforms like Vercel don’t support long-lived WebSocket connections, as functions are short-lived.

 

The Solution:

To manage WebSocket connections reliably:

  1. Create a Singleton WebSocket Server: Ensure only one WebSocket server instance exists.
  2. Use a Custom WebSocket Server with HTTP Integration: Integrate WebSocket with the existing HTTP server.
  3. Opt for External WebSocket Services (if necessary): Use external services like Pusher or Socket.io for scalability.

Set Up a Singleton Server in /pages/api/socket.js:

import { Server } from ‘ws’;

let wss;

export default function handler(req, res) {
if (!wss) {
wss = new Server({ noServer: true });

wss.on(‘connection’, (ws) => {
ws.on(‘message’, (message) => {
console.log(‘Received:’, message);
});

ws.send(‘Connection established’);
});

console.log(‘WebSocket server initialized’);
}

res.end();
}

Integrate WebSocket with the HTTP Server:

Update your custom server.js to handle WebSocket connections:

const { createServer } = require(‘http’);
const next = require(‘next’);
const { parse } = require(‘url’);
const { WebSocketServer } = require(‘ws’);

const app = next({ dev: process.env.NODE_ENV !== ‘production’ });
const handle = app.getRequestHandler();

app.prepare().then(() => {
const server = createServer((req, res) => {
const parsedUrl = parse(req.url, true);
handle(req, res, parsedUrl);
});

const wss = new WebSocketServer({ server });

wss.on(‘connection’, (ws) => {
ws.on(‘message’, (message) => {
console.log(‘Received:’, message);
});

ws.send(‘Hello WebSocket’);
});

server.listen(3000, () => {
console.log(‘> Ready on http://localhost:3000’);
});
});

Explanation

  • Presets: The next/babel preset ensures compatibility with Next.js.
  • Plugins: @babel/plugin-proposal-decorators adds support for decorators, and @babel/plugin-proposal-class-properties ensures class property support.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top