Next.js 14 Real-time Updates with Socket.IO and Prisma Guide

Fabrice KWIZERA - Aug 9 - - Dev Community

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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');
  });
});
Enter fullscreen mode Exit fullscreen mode

Update your package.json to use this custom server:

"scripts": {
  "dev": "node server.js",
  "build": "next build",
  "start": "NODE_ENV=production node server.js"
}
Enter fullscreen mode Exit fullscreen mode

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;
};
Enter fullscreen mode Exit fullscreen mode

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;
};
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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 });
  }
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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.

.
Terabox Video Player