Next.js Discord

Discord Forum

realtime twitch overlay update

Answered
JCo posted this in #help-forum
Open in Discord
Avatar
JCoOP
If I have a next site with an api on it that is triggered by an app, discord bot or game action separate of the site, but it live updates a twitch overlay (expected behaviour), what is the best way to keep the overlay up to date? Webhooks based on the player id? Or something else?
Answered by linesofcode
you can also checkout a service called pusher
View full answer

32 Replies

Avatar
JCoOP
import { getSession } from "@/lib/dbFunctions/session";
import { getDivision } from "@/lib/utils";

interface OverlayPageProps {
    params: {
        id: string;
    };
}

const getStats = async (discordId: string) => {
    const session = await getSession(
        discordId,
        Number(process.env.CURR_SEASON!)
    );

    if (!session)
        return {
            w: 0,
            l: 0,
            sr: 0,
            division: "Bronze 1",
        };

    const division = getDivision(session.currentSr);

    return {
        w: session.wins,
        l: session.losses,
        sr: session.currentSr,
        division,
    };
};

const OverlayPage = async ({ params }: OverlayPageProps) => {
    const { id } = params;

    const { w, l, sr, division } = await getStats(id);

    return id === "415067295715557376" ? (
        <div className="flex flex-row text-6xl gap-0 w-full ">
            <div className="p-4 flex flex-col">
                <p>
                    W: {w} / L: {l}
                </p>
                <p>SR: {sr}</p>
                <p>{division}</p>
            </div>
        </div>
    ) : (
        <div className="flex flex-row text-6xl gap-3">Coming Soon</div>
    );
};

export default OverlayPage;
This is the code for the page, tracking the stats etc. But I don't know the best way to modify this so that it updates in near realtime. I asked ChatGPT and it said SSE and Websockets. But which is the better option for this usecase?
Avatar
If the twitch overlay data is something you can put into nextjs unstable cache
Then your webhook can invalidate that cache key
Avatar
JCoOP
I don’t think it is. It makes a db call to get the session, then when it’s changed I want it to re-fetch the data from the db then update what’s displayed. So websockets?
Avatar
@linesofcode If the twitch overlay data is something you can put into nextjs unstable cache
Avatar
JCoOP
Is this a good solution or would you recommend a better option? given a discord bot currently runs the update session route on the api, then the data needs to update on the overlay for the correct user.
Image
Avatar
where is the data from the twitch overlay coming from
Avatar
JCoOP
A database call
Avatar
then the solution I provided above will work
Avatar
JCoOP
export const getSession = async (
discordId: Session["userId"],
season: Session["season"]
) => {
const session = await db.session.findFirst({
where: {
userId: discordId,
season,
},
});

return session;
};
Avatar
if you put it through a caching layer
Avatar
JCoOP
Do you have any docs I can review to understand that concept?
what chatgpt says about polling will work too
but if you care about your traffic and need realtime it's not a feasible solution
websockets will work too but not if you're deploying to a serverless infrastracture
Avatar
JCoOP
Need realtime
Avatar
unless you spin up a websocket server
Avatar
JCoOP
And I deploy to vercel
Avatar
personally
I would just do polling
Avatar
JCoOP
To call my getSession() function?
Avatar
if your traffic is negligble
Avatar
JCoOP
probably 1000 users at max as its a paid feature of a new discord bot.
But I want to build ahead to scalability, which is unstable_cache I presume?
Avatar
no
if you want to build something scalable
then you need websockets
and a dedicated websocket server(s)
Avatar
you can also checkout a service called pusher
Answer
Avatar
which is websockets as a service
Avatar
JCoOP
I’ll use pusher then
Avatar
JCoOP
"use client";

import { getSession } from "@/lib/dbFunctions/session";
import { pusherClient } from "@/lib/pusher/client";
import { getDivision } from "@/lib/utils";
import { useEffect, useState } from "react";

interface OverlayPageProps {
    params: {
        id: string;
    };
}

const OverlayPage = async ({ params }: OverlayPageProps) => {
    const { id } = params;
    const [stats, setStats] = useState({
        w: 0,
        l: 0,
        sr: 0,
        division: "Bronze 1",
    });

    const getStats = async (discordId: string) => {
        const session = await getSession(
            discordId,
            Number(process.env.CURR_SEASON!)
        );
        if (session) {
            setStats({
                w: session.wins,
                l: session.losses,
                sr: session.currentSr,
                division: getDivision(session.currentSr),
            });
        }
    };

    useEffect(() => {
        // Fetch initial stats
        getStats(id);

        // Subscribe to a channel specific to the user
        const channel = pusherClient.subscribe(`user-${id}`);

        // Listen for an event with updated stats
        channel.bind(
            "stats-updated",
            (data: { w: number; l: number; sr: number }) => {
                setStats({
                    w: data.w,
                    l: data.l,
                    sr: data.sr,
                    division: getDivision(data.sr),
                });
            }
        );

        return () => {
            channel.unsubscribe();
            pusherClient.disconnect();
        };
    }, [id]);

    return id === "415067295715557376" ? (
        <div className="flex flex-row text-6xl gap-0 w-full ">
            <div className="p-4 flex flex-col">
                <p>
                    W: {stats.w} / L: {stats.l}
                </p>
                <p>SR: {stats.sr}</p>
                <p>{stats.division}</p>
            </div>
        </div>
    ) : (
        <div className="flex flex-row text-6xl gap-3">Coming Soon</div>
    );
};

export default OverlayPage;
Seems to work