Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Socket.io with Next.js

Background

I'm trying to establish and connect a socket using Next.js. I'm following the standard guide that uses an API call to test if a socket server is already running and creates one if not.

I have an API script at pages/api/socket/io that looks like this:

import { Server } from "socket.io";

export default function SocketHandler(req, res) {

    if (res.socket.server.io) {
        console.log("Already set up");
        res.end();
        return;
    }

    const io = new Server(res.socket.server);

    // Event handler for client connections
    io.on('connection', (socket) => {
        const clientId = socket.id;
        console.log('A client connected');
        console.log(`A client connected. ID: ${clientId}`);
        io.emit('client-new', clientId);

        // Event handler for receiving messages from the client
        socket.on('message', (data) => {
            console.log('Received message:', data);
        });

        // Event handler for client disconnections
        socket.on('disconnect', () => {
            console.log('A client disconnected.');
        });
    });

    res.socket.server.io = io;
    res.end();
}

I have a page under pages/chat.js that pings /api/socket/io to make sure the socket server is running, then attempts to connect to it.

import React, { useEffect, useState } from "react";
import io from "socket.io-client";

let socket;

const Page = () => {
    const [message, setMessage] = useState("");
    const [username, setUsername] = useState("");

    useEffect(() => {
        socketInitializer();

        return () => {
            socket.disconnect();
        };
    }, []);

    async function socketInitializer() {

        // ping the server to setup a socket if not already running
        await fetch("/api/socket/io");

        // Setup the Socket 
        socket = io({ path: "/api/socket/ping" });

        // Standard socket management
        socket.on('connect', () => {
            console.log('Connected to the server');
        });

        socket.on('disconnect', () => {
            console.log('Disconnected from the server');
        });

        socket.on('connect_error', (error) => {
            console.log('Connection error:', error);
        });

        socket.on('reconnect', (attemptNumber) => {
            console.log('Reconnected to the server. Attempt:', attemptNumber);
        });

        socket.on('reconnect_error', (error) => {
            console.log('Reconnection error:', error);
        });

        socket.on('reconnect_failed', () => {
            console.log('Failed to reconnect to the server');
        });

        // Manage socket message events
        socket.on('client-new', (message) => {
            console.log("new client", message);
        });

        socket.on('message', (message) => {
            console.log("Message", message);
        });

        socket.on('client-count', (count) => {
            console.log("clientCount", count)
        });

    }

    function handleSubmit(e) {
        e.preventDefault();

        socket.emit("message", {
            username,
            message
        });

        setMessage("");
    }

    return (
        <div>
            <h1>Chat app</h1>
            <h1>Enter a username</h1>

            <input value={username} onChange={(e) => setUsername(e.target.value)} />

            <div>
                <form onSubmit={handleSubmit}>
                    <input
                        name="message"
                        placeholder="enter your message"
                        value={message}
                        onChange={(e) => setMessage(e.target.value)}
                        autoComplete={"off"}
                    />
                </form>
            </div>
        </div>
    );
};

export default Page;

/api/socket/ping is a simple script that doesn't do much beyond return ok status. I'm assuming it should check that the server is running and hold any logic to check the client is still valid in the future, but it shouldn't be preventing the client from connecting at this point.

It looks like this:

export default async function handler(req, res) {
    return res.status(200).send("OK");
}

Problem

The issue I'm having is that it seems the socket server is getting set up. However, I can't get the client to connect.

I'm getting an error that suggests I may need to provide some additional configuration options as part of the client setup socket = io({ path: "/api/socket/ping" });, but I'm not sure where to start.

I'm running the server on localhost port 3000. This is the error I'm getting from :

Connection error: Error: server error
    at Socket.onPacket (socket.js:318:1)
    at Emitter.emit (index.mjs:136:1)
    at Polling.onPacket (transport.js:97:1)
    at callback (polling.js:115:1)
    at Array.forEach (<anonymous>)
    at Polling.onData (polling.js:118:22)
    at Emitter.emit (index.mjs:136:1)
    at Request.onLoad (polling.js:361:1)
    at xhr.onreadystatechange (polling.js:297:1)

Request

Please explain why the client is not connecting and make recommendations to fix the issue.

like image 464
Levi Putna Avatar asked May 25 '26 22:05

Levi Putna


2 Answers

import { Server } from "socket.io";
import cors from "cors";
import nextConnect from "next-connect";

const handler = nextConnect();

// Enable CORS
handler.use(cors());

handler.all((req, res) => {
  if (res.socket.server.io) {
    console.log("Already set up");
    res.end();
    return;
  }

  const io = new Server(res.socket.server);

  // Event handler for client connections
  io.on("connection", (socket) => {
    const clientId = socket.id;
    console.log("A client connected");
    console.log(`A client connected. ID: ${clientId}`);
    io.emit("client-new", clientId);

    // Event handler for receiving messages from the client
    socket.on("message", (data) => {
      console.log("Received message:", data);
    });

    // Event handler for client disconnections
    socket.on("disconnect", () => {
      console.log("A client disconnected.");
    });
  });

  res.socket.server.io = io;
  res.end();
});

export default handler;
like image 66
MXX Avatar answered May 28 '26 14:05

MXX


@Louis answer was correct in identifying CORS as the issue. I approached the solution a little differently and thought it would be worth sharing my approach.

I added cors to my project yarn add cors. Then modified my io.js API endpoint to look like this:

io.js

import { Server } from "socket.io";
import cors from 'cors';

// Create a new instance of the CORS middleware
const corsMiddleware = cors();

export default function SocketHandler(req, res) {
    if (res.socket.server.io) {
        console.log("Already set up");
        res.end();
        return;
    }

    const io = new Server(res.socket.server, {
        path: "/api/socket/ping",
        addTrailingSlash: false
    });

    // Event handler for client connections
    io.on('connection', (socket) => {
        const clientId = socket.id;
        console.log('A client connected');
        console.log(`A client connected. ID: ${clientId}`);
        io.emit('client-new', clientId);

        // Event handler for receiving messages from the client
        socket.on('message', (data) => {
            console.log('Received message:', data);
        });

        // Event handler for client disconnections
        socket.on('disconnect', () => {
            console.log('A client disconnected.');
        });
    });

    // Apply the CORS middleware to the request and response
    corsMiddleware(req, res, () => {
        res.socket.server.io = io;
        res.end();
    });
}
like image 45
Levi Putna Avatar answered May 28 '26 15:05

Levi Putna



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!