1. Set up the project
First, ensure you have a Next.js 14 project set up with Prisma and MySQL. If not, create a new project:
npx create-next-app@latest my-realtime-app
cd my-realtime-app
npm install prisma @prisma/client
npx prisma init
Configure your prisma/schema.prisma
file with your MySQL connection and define your models.
2. Install Socket.IO
Install Socket.IO for both server and client:
npm install socket.io socket.io-client
3. Set up the Socket.IO server
Create a custom server file server.js
in the root of your project:
const { createServer } = require('http');
const { parse } = require('url');
const next = require('next');
const { Server } = require("socket.io");
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = createServer((req, res) => {
const parsedUrl = parse(req.url, true);
handle(req, res, parsedUrl);
});
const io = new Server(server);
io.on('connection', (socket) => {
console.log('A user connected');
socket.on('disconnect', () => {
console.log('User disconnected');
});
// Add your Socket.IO event handlers here
});
server.listen(3000, (err) => {
if (err) throw err;
console.log('> Ready on http://localhost:3000');
});
});
Update your package.json
to use this custom server:
"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "NODE_ENV=production node server.js"
}
4. Create a Socket.IO client wrapper
Create a new file lib/socket.js
:
import { io } from 'socket.io-client';
let socket;
export const initSocket = () => {
socket = io(process.env.NEXT_PUBLIC_SOCKET_URL || 'http://localhost:3000');
return socket;
};
export const getSocket = () => {
if (!socket) {
throw new Error('Socket not initialized. Call initSocket first.');
}
return socket;
};
5. Set up a context provider for Socket.IO
Create a new file components/SocketProvider.js
:
'use client';
import React, { createContext, useContext, useEffect, useState } from 'react';
import { initSocket, getSocket } from '../lib/socket';
const SocketContext = createContext(null);
export function SocketProvider({ children }) {
const [socket, setSocket] = useState(null);
useEffect(() => {
const socketInstance = initSocket();
setSocket(socketInstance);
return () => {
socketInstance.disconnect();
};
}, []);
return (
<SocketContext.Provider value={socket}>
{children}
</SocketContext.Provider>
);
}
export const useSocket = () => {
const socket = useContext(SocketContext);
if (!socket) {
throw new Error('useSocket must be used within a SocketProvider');
}
return socket;
};
6. Wrap your app with the SocketProvider
Update your app/layout.js
:
import { SocketProvider } from '../components/SocketProvider';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<SocketProvider>
{children}
</SocketProvider>
</body>
</html>
);
}
7. Create an API route for real-time updates
Create a new file app/api/update/route.js
:
import { NextResponse } from 'next/server';
import { PrismaClient } from '@prisma/client';
import { Server as SocketServer } from "socket.io";
const prisma = new PrismaClient();
export async function POST(req) {
const data = await req.json();
try {
// Update your database
const updatedItem = await prisma.yourModel.update({
where: { id: data.id },
data: data,
});
// Emit the update to all connected clients
const io = new SocketServer(res.socket.server);
io.emit('itemUpdated', updatedItem);
return NextResponse.json({ success: true, data: updatedItem });
} catch (error) {
return NextResponse.json({ success: false, error: error.message }, { status: 500 });
}
}
8. Use real-time updates in a client component
Create a new client component, e.g., components/RealtimeList.js
:
'use client';
import { useState, useEffect } from 'react';
import { useSocket } from './SocketProvider';
export default function RealtimeList() {
const [items, setItems] = useState([]);
const socket = useSocket();
useEffect(() => {
// Fetch initial data
fetchItems();
// Listen for real-time updates
socket.on('itemUpdated', (updatedItem) => {
setItems((prevItems) =>
prevItems.map((item) =>
item.id === updatedItem.id ? updatedItem : item
)
);
});
return () => {
socket.off('itemUpdated');
};
}, [socket]);
const fetchItems = async () => {
// Fetch items from your API
const response = await fetch('/api/items');
const data = await response.json();
setItems(data);
};
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
9. Use the real-time component in a page
Update your app/page.js
:
import RealtimeList from '../components/RealtimeList';
export default function Home() {
return (
<main>
<h1>Real-time Updates</h1>
<RealtimeList />
</main>
);
}
This setup allows you to achieve real-time updates in your Next.js 14 application using Socket.IO, while leveraging server components for initial data fetching and client components for real-time interactions.
Top comments (1)
Great post. I will use this to create a log file recording important events that occurs in my application.