How to fix, nextjs pages refetching data after certain time passed when using graphql
Unanswered
Red imported fire ant posted this in #help-forum
Red imported fire antOP
I have this nextjs 14 project and every page using graphql to fetch the data. And i deployed it, after visiting some pages through the navbar menu page does load on the first as usual(which is correct i assume). And then i click on some other navbar menu items and go back to the one i have visited. This time it loads immediately, but sometimes(after certain time passed) and i revisit the page that i have already visited before, boom it refetches data. How do i fix this? is there any idea why this is happening.
42 Replies
Black-throated Blue Warbler
need to see your fetch functions to determine how you are configuring the cacheing
"sometimes(after certain time passed) and i revisit the page that i have already visited before, boom it refetches data. "
Sounds like your cache is expiring š
Sounds like your cache is expiring š
Red imported fire antOP
@Black-throated Blue Warbler thank you for the reply, here is my graphql-request configuration:
import { GraphQLClient } from "graphql-request";
import { cache } from "react";
import { getSdk } from "@/gql"; // graphql-codegen
const CACHE_TIME = 60 * 60 * 1000; // 1 hour in milliseconds
const client = new GraphQLClient(
${process.env.NEXT_PUBLIC_BACKEND_URL}/graphql,
{
fetch: cache(async (url, params) =>
fetch(url, { ...params, next: { revalidate: CACHE_TIME } }),
),
},
);
const sdk = getSdk(client);
export default sdk;
Black-throated Blue Warbler
hmm, well first issue is your cache time calculation, i think next revalidate uses seconds not miliseconds
so just put 3600 if you want 1hr
you could try setting the revlaidate for all requests on layout or page with:
export const revalidate = 3600 // revalidate at most every hour
export const revalidate = 3600 // revalidate at most every hour
Red imported fire antOP
but isn't
const revalidate = 3600
only work for GET request ?Red imported fire antOP
@Black-throated Blue Warbler thing is i didnt add any revalidate or custom fetch or cache before and project acts as i described. So then i tried to add those to fix this
Black-throated Blue Warbler
oh right.. graphql use post by default
Red imported fire antOP
yeah, š¦ been struggling to find the solution to this
Black-throated Blue Warbler
you have to setup your own cacheing most likely, i think you're on the right track but you need a function that tries to fetch the data from cache first
think you'd need a setup like this:
where it attempts fetching from your cache before firing the post request
const client = new GraphQLClient(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/graphql`,
{
fetch: cache(async (url, params) => {
const cacheKey = JSON.stringify({ url, params });
const cachedResponse = getCachedResponse(cacheKey);
if (cachedResponse) {
return new Response(JSON.stringify(cachedResponse), { status: 200 });
}
const response = await fetch(url, { ...params, method: "POST" });
const data = await response.json();
setCachedResponse(cacheKey, data, CACHE_TIME);
return new Response(JSON.stringify(data), { status: 200 });
}),
}
);
where it attempts fetching from your cache before firing the post request
Red imported fire antOP
Where do i save the cache in here ? like
setCachedResponse
Black-throated Blue Warbler
Warning: Untested GPT response
import { GraphQLClient } from "graphql-request";
import { cache } from "react";
import { getSdk } from "@/gql"; // graphql-codegen
const CACHE_TIME = 60 * 60 * 1000; // 1 hour in milliseconds
// Create a cached fetch function
const cachedFetch = cache(async (url, params) => {
const cacheKey = JSON.stringify({ url, params });
const cachedResponse = getCachedResponse(cacheKey);
if (cachedResponse) {
return new Response(JSON.stringify(cachedResponse), { status: 200 });
}
const response = await fetch(url, { ...params, method: "POST" });
const data = await response.json();
setCachedResponse(cacheKey, data, CACHE_TIME);
return new Response(JSON.stringify(data), { status: 200 });
});
const client = new GraphQLClient(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/graphql`,
{
fetch: cachedFetch,
}
);
const sdk = getSdk(client);
export default sdk;
// In-memory cache utility functions
const cacheStorage = new Map();
const getCachedResponse = (key) => {
const cached = cacheStorage.get(key);
if (!cached) return null;
const now = Date.now();
if (now > cached.expiry) {
cacheStorage.delete(key);
return null;
}
return cached.value;
};
const setCachedResponse = (key, value, ttl) => {
const now = Date.now();
cacheStorage.set(key, { value, expiry: now + ttl });
};
export { getCachedResponse, setCachedResponse };
Red imported fire antOP
Wait a minute, so post request does not have params and url will be always same right ?
Black-throated Blue Warbler
post request should have some params like the body, log it and see
Red imported fire antOP
Sorry but don't know why, returning new instance of Response with data or just data directly giving some strange error :
Error: GraphQL Error (Code: 200): {"response":{"error":"{\"data\":{\"HomePageContent\":{\"data\":{\"attributes\":{\"seo\":
@Black-throated Blue Warbler post request should have some params like the body, log it and see
Red imported fire antOP
yes you are correct, params does have body, so then cachekey works
One thing i couldn't figure out is what structure of the data fetch is returning, it seems when i remove the fetch overriding line it worked fine. But when i add it back this strange error thrown
Black-throated Blue Warbler
seems like its already json and doesn't need to be stringified
try
return new Response(data, { status: 200 });
Red imported fire antOP
yup, tried that and still same
Black-throated Blue Warbler
can i see your full code? are you throwing an error anywhere?
Red imported fire antOP
import { GraphQLClient } from "graphql-request";
import { getSdk } from "@/gql";
const CACHE_TIME = 60 * 60 * 1000;
const cacheStorage = new Map();
const getCachedResponse = (key) => {
const cached = cacheStorage.get(key);
if (!cached) return null;
const now = Date.now();
if (now > cached.expiry) {
cacheStorage.delete(key);
return null;
}
return cached.value;
};
const setCachedResponse = (key, value, ttl) => {
const now = Date.now();
cacheStorage.set(key, { value, expiry: now + ttl });
};
const customFetch = async (url, params) => {
const cacheKey = JSON.stringify({ url, params });
const cachedResponse = getCachedResponse(cacheKey);
if (cachedResponse) {
return new Response(JSON.stringify(cachedResponse), { status: 200 });
}
const response = await fetch(url, { ...params, method: "POST" });
// Check if response is not ok
if (!response.ok) {
throw new Error(`Network response was not ok: ${response.statusText}`);
}
// Attempt to parse response data
const data = await response.json();
// Check for GraphQL errors
if (data.errors) {
console.error("GraphQL errors:", data.errors);
throw new Error(`GraphQL Error: ${JSON.stringify(data.errors)}`);
}
// Cache the response data
setCachedResponse(cacheKey, data, CACHE_TIME);
console.log("Set cache success");
return new Response(data, {status: 200});
};
const client = new GraphQLClient(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/graphql`,
{
fetch: customFetch,
},
);
const sdk = getSdk(client);
export default sdk;
Black-throated Blue Warbler
well right here is throwing that error:
// Check for GraphQL errors
if (data.errors) {
console.error("GraphQL errors:", data.errors);
throw new Error(`GraphQL Error: ${JSON.stringify(data.errors)}`);
}
instead of throwing the error just log it so we can read it
Red imported fire antOP
I just checked and seems its not this error throwing
Black-throated Blue Warbler
you sure you didn't cache that error? haha
comment out this part too for now
if (cachedResponse) {
return new Response(JSON.stringify(cachedResponse), { status: 200 });
}
Red imported fire antOP
haha, it was nt' cache does save, I just commented out and still same. but i am suspecting returning response has some issue because success log printed and then error raised
Set cache success { data: { componentHomePageContent: { data: [Object] } } }
⨯ Error: GraphQL Error (Code: 200): {"response":{"error":"[object Object]","status":200,"headers":
{}},"request":{"query":"query getHomePageContent {\n componentHomePageContent {\n data {\n
attributes {\n seo {\n title\n meta_robots\n meta_discription\n
link_canonical\n }\n Mission_block {\n
Black-throated Blue Warbler
what does it look like when you console log data
from:
const data = await response.json();
const data = await response.json();
Red imported fire antOP
and when i log the data
{ data: { componentHomePageContent: { data: [Object] } } }
Black-throated Blue Warbler
hmm well that looks valid, and i don't see an errors key
try just returning data
Red imported fire antOP
Error: Cannot read properties of undefined (reading 'forEach')
And my page not even have any forEach š
And my page not even have any forEach š
Black-throated Blue Warbler
I stopped using graphql for simple projects a while back, it just creates too much complexity. Is there a good reason for you to be using graphql on this site? just curious
Red imported fire antOP
Backend only provides graphql
Black-throated Blue Warbler
i'd say thats a pretty solid reason then, i think you'll have to do some digging on how to setup graphql-tag with the latest next.js cacheing. Unfortuantely i'm not very experienced with it.
When I was using graphql on my projects I just wrote my own fetch wrapper and didn't use any libraries
When I was using graphql on my projects I just wrote my own fetch wrapper and didn't use any libraries
Red imported fire antOP
OK thank you any way
Black-throated Blue Warbler
example of what i mean by using graphql without library (with next.js custom fetch)
const { data } = await fetch(process.env.GRAPHQL_API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
query: `
query getPosts {
posts {
edges {
node {
title
excerpt
slug
date
}
}
}
}
`,
}),
next: { revalidate: 10 },
}).then((res) => res.json());
could slap a query in there just to see if it works with the revalidate like it should