Next.js provides serverless API routes. By creating a file under ./pages/api
you can have your service running, and I want to have a Socket.io service by using this mechanism.
I have created a client:
./pages/client.js
import { useEffect } from 'react'; import io from 'socket.io-client'; export default () => { useEffect(() => { io('http://localhost:3000', { path: '/api/filename' }); }, []); return <h1>Socket.io</h1>; }
And an API route:
./pages/api/filename.js
const io = require('socket.io')({ path: '/api/filename' }); io.onconnection = () => { console.log('onconnection'); } io.on('connect', () => { console.log('connect'); }) io.on('connection', () => { console.log('connection'); }) export default (req, res) => { console.log('endpoint'); }
But I can't get the client to connect to the Socket.io server and succesfully see any of: 'onconnection'
, 'connect'
, or 'connection'
printed.
Socket.io architecture In order to establish dual-side communication, we need a server and a client. Since we're using NextJS our server-side will be placed in the NextJS API folder, while the client-side will be coded into each page that will need it.
Although Socket.IO indeed uses WebSocket for transport when possible, it adds additional metadata to each packet. That is why a WebSocket client will not be able to successfully connect to a Socket.IO server, and a Socket.IO client will not be able to connect to a plain WebSocket server either.
Serverless Functions on Vercel are stateless and have a maximum execution duration. As a result, it is not possible to maintain a WebSocket connection to a Serverless Function.
The trick is to plug 'socket.io' into the http server only once, so checking every access to the api. Try something like this:
./pages/api/socketio.js
import { Server } from 'socket.io' const ioHandler = (req, res) => { if (!res.socket.server.io) { console.log('*First use, starting socket.io') const io = new Server(res.socket.server) io.on('connection', socket => { socket.broadcast.emit('a user connected') socket.on('hello', msg => { socket.emit('hello', 'world!') }) }) res.socket.server.io = io } else { console.log('socket.io already running') } res.end() } export const config = { api: { bodyParser: false } } export default ioHandler
./pages/socketio.jsx
import { useEffect } from 'react' import io from 'socket.io-client' export default () => { useEffect(() => { fetch('/api/socketio').finally(() => { const socket = io() socket.on('connect', () => { console.log('connect') socket.emit('hello') }) socket.on('hello', data => { console.log('hello', data) }) socket.on('a user connected', () => { console.log('a user connected') }) socket.on('disconnect', () => { console.log('disconnect') }) }) }, []) // Added [] as useEffect filter so it will be executed only once, when component is mounted return <h1>Socket.io</h1> }
You have to have the /api/pusher/auth
to authenticate with pusher on the frontend. Then you use the key you get from that to communicate with pusher. It's for security purposes. You can do it all through the frontend, but depending on your app, if you're saving data (such as messages, or chats) then probably should authenticate.
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