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.
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;
@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();
});
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With