How to cache data with server actions
Answered
JChicano posted this in #help-forum

JChicanoOP
Hello everyone, I'm performing my queries using server actions. The process is illustrated in the images below. I know that if you use fetch the data is cached by default, but with server actions this doesnt happend, what should I do?


Answered by JChicano
{cache: 'force-cache',
next: { tags: ['public-posts'], revalidate: 3600 }
It was this line, i thought that the method GET cached by default, but without this is not doing it26 Replies

@JChicano Hello everyone, I'm performing my queries using server actions. The process is illustrated in the images below. I know that if you use fetch the data is cached by default, but with server actions this doesnt happend, what should I do?

server actions shouldn't be used to fetch data. Technically it is possible, but in the first way they are made to mutate data serverside.
Another thing that you want to use is a clientside fetching library. Without it you can lose control over your data pretty fast. SWR or React Query are examples.
Another thing that you also need to know is that you should fetch data serverside and then pass it via props to the client. That makes your page load faster
To solve your issue: use
Another thing that you want to use is a clientside fetching library. Without it you can lose control over your data pretty fast. SWR or React Query are examples.
Another thing that you also need to know is that you should fetch data serverside and then pass it via props to the client. That makes your page load faster
To solve your issue: use
unstable_cache
to cache third party results
JChicanoOP
Anyway, I pass the information to the client component via props since it is wrapped in a <Suspense>, but what I should do is use React Query for queries (selects), and for inserts, deletes, or updates, I can continue using server actions, right?

@B33fb0n3 server actions shouldn't be used to fetch data. Technically it is possible, but in the first way they are made to mutate data serverside.
Another thing that you want to use is a clientside fetching library. Without it you can lose control over your data pretty fast. SWR or React Query are examples.
Another thing that you also need to know is that you should fetch data serverside and then pass it via props to the client. That makes your page load faster
To solve your issue: use unstable_cache to cache third party results

JChicanoOP
And with React Query, since the results come through a fetch, would they be cached automatically? Could you give me an example of how to make that query from a server component, like the main page page.tsx?

@JChicano And with React Query, since the results come through a fetch, would they be cached automatically? Could you give me an example of how to make that query from a server component, like the main page page.tsx?

yes, you can fetch on the server side directly and pass the results to the client:
export default async function Page() {
const data = await fetch('https://your.api/your/endpoint')
const posts = await data.json()
// or directly render them and move the code that needs interactivity into it's own component and mark only them as client component
return (
<Posts posts={posts}/>
)
}

@B33fb0n3 yes, you can fetch on the server side directly and pass the results to the client:
tsx
export default async function Page() {
const data = await fetch('https://your.api/your/endpoint')
const posts = await data.json()
// or directly render them and move the code that needs interactivity into it's own component and mark only them as client component
return (
<Posts posts={posts}/>
)
}

JChicanoOP
Ok, Ty mate, as always you help me a lot π

happy to help

Transvaal lion
what worked for me was to have an /api/ endpoint and some helper functions for grabbing the data. Then the requests get cached

@B33fb0n3 yes, you can fetch on the server side directly and pass the results to the client:
tsx
export default async function Page() {
const data = await fetch('https://your.api/your/endpoint')
const posts = await data.json()
// or directly render them and move the code that needs interactivity into it's own component and mark only them as client component
return (
<Posts posts={posts}/>
)
}

JChicanoOP
Hello again π
, im doing all the stuff with the fetch and i think that is not catching the response it can be possible?
I added a console.log to the database query, and it executes every time, this indicate that it is not being cached right?

Transvaal lion
you could add your own headers to the req
export async function GET(req, { params }) {
try {
const { slug } = await params;
const { article, articleError } = await fetchSingleArticle(slug);
if (articleError) {
return new Response(JSON.stringify({ error: articleError.message }), {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
}
return new Response(JSON.stringify({ article }), {
status: 200,
headers: {
'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=59',
'Content-Type': 'application/json',
},
});
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
}
}
try {
const { slug } = await params;
const { article, articleError } = await fetchSingleArticle(slug);
if (articleError) {
return new Response(JSON.stringify({ error: articleError.message }), {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
}
return new Response(JSON.stringify({ article }), {
status: 200,
headers: {
'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=59',
'Content-Type': 'application/json',
},
});
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
}
}

@Transvaal lion export async function GET(req, { params }) {
try {
const { slug } = await params;
const { article, articleError } = await fetchSingleArticle(slug);
if (articleError) {
return new Response(JSON.stringify({ error: articleError.message }), {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
}
return new Response(JSON.stringify({ article }), {
status: 200,
headers: {
'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=59',
'Content-Type': 'application/json',
},
});
} catch (error) {
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: {
'Content-Type': 'application/json',
},
});
}
}

JChicanoOP
How do you pass parameters to a GET request? Since these can't have a body.

Transvaal lion
export const apiFetchPosts = async ( { category, subcategory, region, search, page, pageSize } ) => {
try {
const req = await fetch(
cache: 'force-cache',
next: { tags: ['public-posts'], revalidate: 3600 }
});
if (!req.ok) {
throw new Error(
}
const response = await req.json();
return response;
} catch (error) {
console.error('Error fetching regions:', error);
throw error;
}
};
try {
const req = await fetch(
${process.env.NEXT_PUBLIC_BASE_URL}/api/get-public-posts?category=${category}&subcategory=${subcategory}®ion=${region}&search=${search}&page=${page}&pageSize=${pageSize}
, {cache: 'force-cache',
next: { tags: ['public-posts'], revalidate: 3600 }
});
if (!req.ok) {
throw new Error(
Failed to fetch Regions: ${req.status}
);}
const response = await req.json();
return response;
} catch (error) {
console.error('Error fetching regions:', error);
throw error;
}
};
just some mumbo jumbo code, but you get the idea

JChicanoOP
Oh, right, in the URL. Thank you so much! I'll give it a try.

JChicanoOP
yep, now its working finalyyyy

@Transvaal lion export const apiFetchPosts = async ( { category, subcategory, region, search, page, pageSize } ) => {
try {
const req = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/get-public-posts?category=${category}&subcategory=${subcategory}®ion=${region}&search=${search}&page=${page}&pageSize=${pageSize}`, {
cache: 'force-cache',
next: { tags: ['public-posts'], revalidate: 3600 }
});
if (!req.ok) {
throw new Error(`Failed to fetch Regions: ${req.status}`);
}
const response = await req.json();
return response;
} catch (error) {
console.error('Error fetching regions:', error);
throw error;
}
};

JChicanoOP
{cache: 'force-cache',
next: { tags: ['public-posts'], revalidate: 3600 }
It was this line, i thought that the method GET cached by default, but without this is not doing itAnswer

Transvaal lion
Indeed, itβs something they changed in nextjs 15. Force cache was on by default in prev versions

JChicanoOP
if the data has to be updated with this it will be do it right?
revalidateTag("user-data");
'pubic-posts' (to continue with the same example)

Transvaal lion
This will clear the next cache but you will see the content change after the time you set in the headers expires i think. Try to test it out

JChicanoOP
Yes, it works like that, but I wanted to force an update when the user modifies their data so that the new changes are reflected immediately.

Transvaal lion
i was also searching for that for a long time and couldn/t make it work, at least not when hosting it on vercel

JChicanoOP
for me with the revalidateTag() function is working, i have it in localhost, you think that hosted in vercel it wont work?

Transvaal lion
Indeed, on vercel you wonβt be able to clear the cache on demand, it will only clear the next cache but the vercel one will wait for the headers expiration time