Next.js Discord

Discord Forum

how can show data of User as advertisement ?

Unanswered
Northeast Congo Lion posted this in #help-forum
Open in Discord
Northeast Congo LionOP
I am trying to show data as advertisement of User from Mongodb database to Next.js 14. I used client side to rotate ads in queue but its not real time.

126 Replies

Northeast Congo LionOP
can anyone help me ?
@joulev
Northeast Congo LionOP
@Chinese Alligator can you help me
Toyger
first of all don't ping users that are not part of topic.
second of all your explanation of your problem is awfull.
add details, what ads, how it shown now, how it should be, any reference to thing that you want to achieve etc...
Northeast Congo LionOP
I tried with useEffect, but its client side fetching and its not real time, I want all advertisement in sync with all users
Toyger
in your code you are not doing anything with your response, why then you even fetch it?
your pusher looks ok, do you get any messages at all through it? did you check is it work at all?
@Toyger in your code you are not doing anything with your response, why then you even fetch it? your pusher looks ok, do you get any messages at all through it? did you check is it work at all?
Northeast Congo LionOP
I am using Next.js 14, I added this code in route file, do I need to use server action instead route file
I mean this , api/advertisement/getAllAds
Its printing all this values from env, so issue is not here
appId: process.env.PUSHER_APP_ID!,
key: process.env.PUSHER_KEY!,
secret: process.env.PUSHER_SECRET!,
cluster: process.env.PUSHER_CLUSTER!,
useTLS: true
Toyger
pusher should have some kind of event logging, also you can add console.log to emitters and subscriptions to check are they working
@Toyger pusher should have some kind of event logging, also you can add console.log to emitters and subscriptions to check are they working
Northeast Congo LionOP
error :
Ad rotation triggered: Response {type: 'basic', url: 'http://localhost:3000/api/advertisement/getAllAds', redirected: false, status: 405, ok: false, …} ....
....................

for Adbox component :
  useEffect(() => {

    fetch('/api/advertisement/getAllAds').then(response => {
      console.log('Ad rotation triggered:', response);
    }).catch(error => {
      console.error('Error triggering ad rotation:', error);
    });


    console.log('Adbox pusher api start')
    const pusher = new Pusher(process.env.NEXT_PUBLIC_PUSHER_KEY!, {
      cluster: process.env.NEXT_PUBLIC_PUSHER_CLUSTER!,
      forceTLS: true
    });

    console.log('Adbox pusher api middle')
    const channel = pusher.subscribe('ad-channel');
    channel.bind('ad-update', function(data:any) {
      console.log('Received ad update:', data); // Check the data received
      setCurrentAd(data.ad);
    });
  
    return () => {
      pusher.unsubscribe('ad-channel');
      channel.unbind_all();
    };
    
  }, []);
For currentAd state :
console.log error : Adbox pusher: null
my issue is I know how to fetch data through route in Next.js but unable to do with pusher
Is my tech stack right ?
I mean pusher for real time data, I mean to fetch
gone
now its 500
error
Toyger
check server logs
Northeast Congo LionOP
wait bro
what do yo mean ?

👇👇
also, from where this url triggers? if from some external source it will not have access to your localhost:3000 ?
Toyger
I don't know where you call it, is it some webhook url, or is it some url that you call in your code.
@Toyger I don't know where you call it, is it some webhook url, or is it some url that you call in your code.
Northeast Congo LionOP
see this is api route file
Toyger
right now you need to check your pusher account and it should have some kind of logs
@Toyger right now you need to check your pusher account and it should have some kind of logs
Northeast Congo LionOP
logs in server side of Next.js
null
 ⨯ node_modules\pusher\lib\errors.js (13:0) @ new RequestErrorunhandledRejection: Error
    at new RequestError (webpack-internal:///(rsc)/./node_modules/pusher/lib/errors.js:13:16)        
    at eval (webpack-internal:///(rsc)/./node_modules/pusher/lib/requests.js:61:17)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Timeout.eval [as _onTimeout] (webpack-internal:///(rsc)/./app/api/advertisement/getAllAds/route.ts:31:13) {
  name: 'PusherRequestError',
  message: 'Unexpected status code 413',
  url: 'https://api-ap2.pusher.com/apps/1791291/events?auth_key=1124eb29e2343d430175&auth_timestamp=1714565710&auth_version=1.0&body_md5=9407ca774dc5539f5b0730b8a1dd27c2&auth_signature=d050297c957aeb4b2617ab130c8c74a0b4e380a73d39baf7a859b8404405380d',
  error: undefined,
  status: 413,
  body: 'The data content of this event exceeds the allowed maximum (10240 bytes). See https://pusher.com/docs/channels/server_api/http-api#publishing-events for more info\n'
}
@Toyger right now you need to check your pusher account and it should have some kind of logs
Northeast Congo LionOP
I am checking with pusher
Toyger
The data content of this event exceeds the allowed maximum (10240 bytes)
they allow only short messagees 10kb
@Toyger `The data content of this event exceeds the allowed maximum (10240 bytes)`
Northeast Congo LionOP
http errors :
The data content of this event exceeds the allowed maximum (10240 bytes). See https://pusher.com/docs/channels/server_api/http-api#publishing-events for more info

Request method: POST
My request method was GET, why its showing POST
this errors are from pusher server logs
@Northeast Congo Lion My request method was GET, why its showing POST
Toyger
you are not controlling request to pusher, you can only call trigger, which will call POST to their api with your data.
they even don't have GET handler.
@Northeast Congo Lion can you explain this
Toyger
it's self explanatory,
you are calling
await pusher.trigger('ad-channel', 'ad-update', {
          ad
        });

which call POST request to pusher API, which show you error that your message bigger than allowed in API limits.
now I am getting response but not image
@Toyger it's self explanatory, you are calling js await pusher.trigger('ad-channel', 'ad-update', { ad }); which call POST request to pusher API, which show you error that your message bigger than allowed in API limits.
Northeast Congo LionOP
before:
Adbox pusher: null


Now (after) :
Adbox pusher: {_id: '6627ab3fadf957dc0af93a11', title: 'Upland', link: 'https://r.upland.me/gKuk', image: '', userId: '662
but I want images also
@Northeast Congo Lion before: Adbox pusher: null Now (after) : Adbox pusher: {_id: '6627ab3fadf957dc0af93a11', title: 'Upland', link: 'https://r.upland.me/gKuk', image: '', userId: '662
Toyger
probably pusher not intended for that, you probably save all of this ads in your database, so you can send via pusher only update event and in your client on message from pusher you again fetch data from api.
I am getting data from backend

 export async function GET(req: Request) {
    try {
      await connectDB();
      const ads = await Advertisement.find({});


      console.log('ads through pusher:', ads)
Toyger
you sent a lot of data, I am saying send only update event
await pusher.trigger('ad-channel', 'ad-update', {upd:1});
Toyger
small update event
Northeast Congo LionOP
my code is :
await pusher.trigger('ad-channel', 'ad-update', {
   ad
});
Toyger
when you get such small update event
you refetching data from your api
@Toyger when you get such small update event
Northeast Congo LionOP
but I already sent only 1 object which is ad
Toyger
which giant object that rejected by pusher because of size
@Toyger which giant object that rejected by pusher because of size
Northeast Congo LionOP
😂 yeah
@Toyger which giant object that rejected by pusher because of size
Northeast Congo LionOP
bro, what is upd
Toyger
so instead you send small message
upd is short for update
you can send whatever you want
it just should be small
@Toyger upd is short for update
Northeast Congo LionOP
oh, like custom message
Toyger
yes
Northeast Congo LionOP
not got this, for currentAd state
Adbox pusher: undefined


import React, { useEffect, useState } from 'react';
import Pusher from 'pusher-js';
import Image from 'next/image';

type AdType = {
    _id: string;
    link: string;
    image: string;
}

const AdBox = () => {
  const [currentAd, setCurrentAd] = useState<AdType | null>(null);

  useEffect(() => {

    fetch('/api/advertisement/getAllAds').then(response => {
      console.log('Ad rotation triggered:', response);
    }).catch(error => {
      console.error('Error triggering ad rotation:', error);
    });

    const pusher = new Pusher(process.env.NEXT_PUBLIC_PUSHER_KEY!, {
      cluster: process.env.NEXT_PUBLIC_PUSHER_CLUSTER!,
      forceTLS: true
    });

    console.log('Adbox pusher api middle')
    const channel = pusher.subscribe('ad-channel');
    channel.bind('ad-update', function(data:any) {
      console.log('Received ad update:', data); // Check the data received
      setCurrentAd(data.ad);
    });
    
    return () => {
      pusher.unsubscribe('ad-channel');
      channel.unbind_all();
    };
  
  console.log('Adbox pusher:', currentAd)
  return (
    <div>
      {currentAd && (
        <div>
          <Image src={currentAd.image} alt="Advertisement" width={300} height={300} layout="responsive" />
          <a href={currentAd.link} target="_blank" rel="noopener noreferrer">Visit Ad</a>
        </div>
      )}
    </div>
  );
};

export default AdBox;
for
Received ad update: {upd: 1}
Toyger
if it's message from console then you get update
now you need to change
 channel.bind('ad-update', function(data:any) {
      console.log('Received ad update:', data); // Check the data received
      setCurrentAd(data.ad);
    });

so it refetch data
fetch('/api/advertisement/getAllAds').then(response => {
      console.log('Ad rotation triggered:', response);
    }).catch(error => {
      console.error('Error triggering ad rotation:', error);
    });

and set it to your state
@Toyger if it's message from console then you get update now you need to change js channel.bind('ad-update', function(data:any) { console.log('Received ad update:', data); // Check the data received setCurrentAd(data.ad); }); so it refetch data js fetch('/api/advertisement/getAllAds').then(response => { console.log('Ad rotation triggered:', response); }).catch(error => { console.error('Error triggering ad rotation:', error); }); and set it to your state
Northeast Congo LionOP
Ad rotation ads in json undefined


updated code
fetch('/api/advertisement/getAllAds')
    .then(response => {
      console.log('Ad rotation triggered:', response);
      response.json()
    })
    .then(data => {
      console.log('Ad rotation ads in json', data)
      setCurrentAd(data);
    })
    .catch(error => {
      console.error('Error triggering ad rotation:', error);
    });
I added ad to pass object through response
route.ts
export async function GET(req: Request) {
    try {
      await connectDB();
      const ads = await Advertisement.find({});


      console.log('ads through pusher:', ads)

      let currentAdIndex = 0;
      setInterval(async () => {
        
        const ad = ads[currentAdIndex];
        console.log(`Sending ad: ${currentAdIndex}`, ad);
        await pusher.trigger('ad-channel', 'ad-update', {
          upd: 1
        });
        currentAdIndex = (currentAdIndex + 1) % ads.length;
      }, 20000);

      return NextResponse.json({ message: 'Ad rotation started', ad: ads[currentAdIndex] });
    } catch (error) {
      console.error('Error rotating ads:', error);
    }
}
Northeast Congo LionOP
its only trigger event, which is pusher.trigger

not fetching data, I mean get request after each trigger
and now its only getting undefined for
Ad rotation ads in json
@Toyger why do you need 2 then here? get data directly from response
Northeast Congo LionOP
I am getting response but its too much responses.

I will share my updated code and screenshot
const fetchAds = async() => {
    try{
      const response = await axios.get('/api/advertisement/getAllAds');
      console.log('response', response?.data)
      setCurrentAd(response?.data?.ad)
    }catch(error){
      console.log('Error fetching ads:', error);
    }
  }

useEffect(() => {
    fetchAds();

    const pusher = new Pusher(process.env.NEXT_PUBLIC_PUSHER_KEY!, {
      cluster: process.env.NEXT_PUBLIC_PUSHER_CLUSTER!,
      forceTLS: true
    });

    const channel = pusher.subscribe('ad-channel');
    channel.bind('ad-update', function(data:any) {
      console.log('Received ad update:', data); 
      fetchAds();
    });
    
    return () => {
      pusher.unsubscribe('ad-channel');
      channel.unbind_all();
    };
        
  }, []);
I changed method from fetch to async await and invoke in useEffect first then also in bind method (fetchAds())
@Toyger what's wrong about it? code looks fine
Northeast Congo LionOP
its making too many request
I will show you
Toyger
based on that you receive a lot ad-update update events, then you probably generating a lot of them
Northeast Congo LionOP
changes I made is added state isFetch and use it in useEffect for if condition of fetchAds invoking
const AdBox = () => {
  const [currentAd, setCurrentAd] = useState<AdType | null>(null);
  const [isFetch, setIsFetch] = useState<boolean>(true);

  const fetchAds = async() => {
    try{
      const response = await axios.get('/api/advertisement/getAllAds');
      setCurrentAd(response?.data?.ad)
    }catch(error){
      console.log('Error fetching ads:', error);
    }
  }

  useEffect(() => {
    if(isFetch){
      fetchAds();
      setIsFetch(false);
    }
    
    const pusher = new Pusher(process.env.NEXT_PUBLIC_PUSHER_KEY!, {
      cluster: process.env.NEXT_PUBLIC_PUSHER_CLUSTER!,
      forceTLS: true
    });

    const channel = pusher.subscribe('ad-channel');
    channel.bind('ad-update', function(data:any) {
      console.log('Received ad update:', data); // Check the data received
      fetchAds();
    });
    
    return () => {
      pusher.unsubscribe('ad-channel');
      channel.unbind_all();
    };
        
  }, []);
  
  console.log('Adbox pusher:', currentAd)
  return (.....
    )
}
Is here I making any mistake, in pusher.trigger?

export async function GET(req: Request) {
    try {
      await connectDB();
      const ads = await Advertisement.find({});

      console.log('ads through pusher:', ads)

      let currentAdIndex = 0;
      setInterval(async () => {
        
        const ad = ads[currentAdIndex];
        console.log(`Sending ad: ${currentAdIndex}`, ad);
        await pusher.trigger('ad-channel', 'ad-update', {
          upd: 1
        });
        currentAdIndex = (currentAdIndex + 1) % ads.length;
      }, 60000);

      console.log('ad res:', ads[currentAdIndex])

      return NextResponse.json({ message: 'Ad rotation started', ad: ads[currentAdIndex] });
    } catch (error) {
      console.error('Error rotating ads:', error);
    }
}
Toyger
why?
Northeast Congo LionOP
I want to invoke data from ad array in queue, like google ads, each object contain link and image, that I want to show on frontend
Toyger
you need to stop your deployment of site because it basically generates thousands of events right now
@Toyger you need to stop your deployment of site because it basically generates thousands of events right now
Northeast Congo LionOP
I haven't deployed that updated code
don't worry
Toyger
wdym? if you receiveing events then pusher works
and eventually it will start charge money
@Toyger wdym? if you receiveing events then pusher works
Northeast Congo LionOP
and eventually it will start charge money ?
Toyger
you generating thousands of events through pusher right now
Northeast Congo LionOP
oh
yes
Toyger
you don't need setinterval, you need to trigger single update event. that's all
after that your client will fetch all new data from server
Northeast Congo LionOP
but how it fetch after each 1 minute
Toyger
you have code
channel.bind('ad-update', function(data:any) {
      console.log('Received ad update:', data); // Check the data received
      fetchAds();
    });

that will fetch it as soon as receiveing upd signal
right now you sending thousands signals and it making thousands fetches
@Toyger you have code js channel.bind('ad-update', function(data:any) { console.log('Received ad update:', data); // Check the data received fetchAds(); }); that will fetch it as soon as receiveing upd signal
Northeast Congo LionOP
but how it fech after 1 minute, I set setInterval, which was wrong approach but it fetches after 1 minute, how it hapen without it
it triggers new request, that I understand but what about timing
Toyger
why 1 minute? from where this 1 minute arise? you wanted update your ad in real time, your code without setinterval will update it in real time
Toyger
why?
Northeast Congo LionOP
ok, I will explain you
I have users data, we can consider 1000 users
and they submit their site link and image or anything they want to promote
and its in database and I am rendering those data as advertisemnet on frontend
if there 1000 Users,
each User's advertisement will run for 1 minute, after that advertisement will change and next User's advertisement will show .

I can change time from 1 minute to 2 minutes.
I use real time cause I want to show advertisement in sync, like A user will see 'xy' advertisement then B user will see same advertsement.

I tried this with client side fetching with only useEffect but it fetches from 1st each time if I open app and If I close it starts from 1st again. So its not syncing real time, right ?
this feature I want to implement
bro, have you got my point ?
for reference this is document data
Toyger
I will not help you with implementation, you have no idea what you working with, you need to read how giant ads networks work, and gain more experience in programming.
I can give you direction, if you want to be in sync you can use user UTC as point in sync, because it will be same for everyone. and setInterval should be run on client. Using websocket for this task is useless waste of resources, especially for syncing.
Northeast Congo LionOP
bro, pls help me, I already waited for so long. No one knows,
atleast now its working
and for senterval, I can remove it from server side, get data in array of 10 list items, then can show on client side with setinterval
is that ok ?
or should I use other option like redis, can I do that with redis ?
Toyger
UTC will give you data in sync, as I said it same for everyone, deviation is insignificant for human eye.
redis or memcached is required.
but your "auction" of banners is bad, your clients will waste their money with such calculations, you need ton of additional logging for fair views calculation. it's not a single pusher websocket task, it's much more because people pay for view.
Northeast Congo LionOP
like this
const updateCurrentAd = (ads: AdType[]) => {
        const minutes = new Date().getUTCMinutes();
        const currentAdIndex = minutes % ads.length;
        setCurrentAd(ads[currentAdIndex]);
    };
const AdBox = () => {
    const [ads, setAds] = useState<AdType[]>([]);
    const [currentAd, setCurrentAd] = useState<AdType | null>(null);

    const fetchAds = async () => {
        try {
            const response = await axios.get('/api/advertisement/getAllAds');
            setAds(response.data.ads);
            updateCurrentAd(response.data.ads);
        } catch (error) {
            console.error('Error fetching ads:', error);
        }
    };

    const updateCurrentAd = (ads: AdType[]) => {
        const minutes = new Date().getUTCMinutes();
        const currentAdIndex = minutes % ads.length;
        setCurrentAd(ads[currentAdIndex]);
    };

    useEffect(() => {
        fetchAds();
        const interval = setInterval(() => {
            if (ads.length > 0) {
                updateCurrentAd(ads);
            }
        }, 1000 * 10); // check every 10 seconds to limit checks

        return () => clearInterval(interval);
    }, [ads]);
Toyger
something like that is minimal setup, but this still not include view amount, because each and every network and device is different it can take like 20s to fetch new data, so you need a lot of additional counter when image change, when it's loaded in DOM, how many user saw it etc......
you need to have your action alghoritm, what if new ad arrived, what if some was deleted etc......
it's freaking rabbit hole.
Northeast Congo LionOP
if its possible on client side with UTC, I will try that.
I just want ads in syncronize for all users
see my implementation, should I do like that
const AdBox = () => {
    const [ads, setAds] = useState<AdType[]>([]);
    const [currentAd, setCurrentAd] = useState<AdType | null>(null);

    const fetchAds = async () => {
        try {
            const response = await axios.get('/api/advertisement/getAllAds2');
            console.log('response:', response)
            setAds(response?.data?.advertisement);
            updateCurrentAd(response?.data?.advertisement); // Initial update after fetch
        } catch (error) {
            console.error('Error fetching ads:', error);
        }
    };

    const updateCurrentAd = (ads: AdType[]) => {
        if(ads.length === 0){
            setCurrentAd(null);
            return;
        }

        const now = new Date();
        const startOfDay = new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
        const millisecondsSinceStartOfDay = now.getTime() - startOfDay.getTime();
        const minutesSinceStartOfDay = Math.floor(millisecondsSinceStartOfDay / 60000);
        const currentAdIndex = minutesSinceStartOfDay % ads?.length;
        setCurrentAd(ads[currentAdIndex]);
    };

    useEffect(() => {
        fetchAds();
    
        // Align to the next full minute for checking
        let interval: any;
        const now = new Date();
        const millisecondsToNextMinute = (60 - now.getSeconds()) * 1000 - now.getMilliseconds();
        setTimeout(() => {
            interval = setInterval(() => {
                updateCurrentAd(ads); // Reference the latest ads through closure
            }, 20000); // Every 60 seconds, precisely at the start of each minute
        }, millisecondsToNextMinute);
    
        return () => clearInterval(interval);
    }, []); // Empty dependency array, run only on mount
    
    console.log('arry', ads)

    return (