Going in circles trying to cache a server component, nothing seem to work, what am I missing?
Answered
Champagne D’Argent posted this in #help-forum
Champagne D’ArgentOP
Next 15, App router.
In my app/page.tsx I have the following
I have two requirements:
1. I want to cache the result of my database call. But I could cache the entire page as well, doesn't matter.
2. I want to be able to invalidate cache on demand
What I have tried so far:
"use cache"
Switched to canary and started using "use cache". That got me into an error that I can't export "force-dynamic", and without force-dynamic I can't build. This API looks the best to me, really wanted it to work
cache flags on fetch
Since I'm not using a fetch, using library that makes db calls (with a fetch I cannot access), this doesn't help me.
export const fetchCache = "force-cache";
Tried with
but that didn't seem to do anything.
What am I missing? It feels like such a simple use case.
In my app/page.tsx I have the following
// Imports...
// I had to have this, otherwise it got an error during build (because it cant do DB calls in build-time I guess)
export const dynamic = "force-dynamic";
// This part I want to control the cache of.
async function getCurrentYear() {
"use server";
const year = await database.getCurrentYear();
if (!year) {
redirect("/inactive");
}
return year;
}
export default async function App() {
const year = await getCurrentYear();
return (
...JSX
I have two requirements:
1. I want to cache the result of my database call. But I could cache the entire page as well, doesn't matter.
2. I want to be able to invalidate cache on demand
What I have tried so far:
"use cache"
Switched to canary and started using "use cache". That got me into an error that I can't export "force-dynamic", and without force-dynamic I can't build. This API looks the best to me, really wanted it to work
cache flags on fetch
Since I'm not using a fetch, using library that makes db calls (with a fetch I cannot access), this doesn't help me.
export const fetchCache = "force-cache";
Tried with
export const fetchCache = "force-cache";
export const revalidate = 120;
but that didn't seem to do anything.
What am I missing? It feels like such a simple use case.
81 Replies
Server actions shouldn't be used for getting items
it should only be used for mutations
use API routes for getting items
Champagne D’ArgentOP
Huh, I'm pretty sure I followed an example from the docs where they had this exact pattern, an async React component that awaited a data fetching server action
And there is no way to cache in server actions, since they're expected to be used for mutations
wait, hold on
Can you send a link
Champagne D’ArgentOP
yeah trying to find it again, was a few days ago
https://nextjs.org/docs/app/building-your-application/data-fetching/fetching
The first example here seems to follow the same pattern?
The first example here seems to follow the same pattern?
Or is that specific to fetch and not for any async javascript function? 🤔
this is not a server action
this is a server component
Champagne D’ArgentOP
fair - I might be using the wrong terminology, new to Next! What's the difference between my code and that example? If nothing, is it possible to cache a server component like I want to?
My
App
is a Server Component (if I got the terminology right 😄 ). I want to either cache the entire server component (with an option to invalidate it) or specifically cache the data awaited in the getCurrentYear
methodYour code is a server action(indicated by use server), and in docs, its server component
Champagne D’ArgentOP
I've tried with both "use server" and without use server, seemed to struggle to find a cache solution still!
if you remove use server, it should work fine.
Champagne D’ArgentOP
Everything works fine except the possibility to cache the darn thing 😦
So I should rather ask the question:
How do I cache a server component?
How do I cache a server component?
(or any data within a server component)
just add
"use cache"
to the topChampagne D’ArgentOP
I've tried use cache, never managed tomake it build
"use cache"
Switched to canary and started using "use cache". That got me into an error that I can't export "force-dynamic", and without force-dynamic I can't build. This API looks the best to me, really wanted it to work
yes
Its not ready for prime to lol
no, its in stable i guess
unstable_cache does work with server actions tho
that is not a good approach
Oh wow, I thought that was only canary, seems premature.
IIRC joulev or someone told me server action can't be run in parallel
same, but when I searched it, even I was a lil shocked
Correct. They cant
they are meant for mutations and you never want to mutate two things at once from the client.... well not never but
Champagne D’ArgentOP
Error: "use cache" is only available with the experimental.dynamicIO config.👉 👉
CanaryOnlyError: The experimental feature "experimental.dynamicIO" can only be enabled when using the latest canary version of Next.js.
Idk if 'use cache' is a better appraoch its still unstable.
Champagne D’ArgentOP
This is what I got when I tried the "use cache" way
What version of nextjs you using?
Champagne D’ArgentOP
"next": "15.0.2",
Easy to enable it.
Champagne D’ArgentOP
Yep, but it requires canary, as you mentioned
CanaryOnlyError: The experimental feature "experimental.dynamicIO" can only be enabled when using the latest canary version of Next.js.
Nope
Champagne D’ArgentOP
That's the error I get at least
Did you enable it in your config?
Champagne D’ArgentOP
⚠ Found a change in next.config.mjs. Restarting the server to apply the changes...
C:\Projects\x\node_modules\next\src\server\config.ts:250
throw new CanaryOnlyError('experimental.dynamicIO')
^
CanaryOnlyError: The experimental feature "experimental.dynamicIO" can only be enabled when using the latest canary version of Next.js.
Oh that is the error not reading lol
Gotcha, so its not in 15.0.2 but its in the docs weird.
I would just give it a few weeks to hit the stable client.
or update to canary and risk it for the biscuit
Champagne D’ArgentOP
Switched to canary and started using "use cache". That got me into an error that I can't export "force-dynamic", and without force-dynamic I can't build. This API looks the best to me, really wanted it to workGot other issues with "use cache", didn't get it to work there either... Idk if unstable or just me 🙂
but kinda gave up hope on "use cache"
Its brand new.... I wouldnt use it. Use unstable_cache.
Answer
Champagne D’ArgentOP
Why isnt
unstable_cache
a good approach?Its a known facotr
Not in server actions
In server components, it is
Meh, if your using server actions for fetching, you already breaking the rule of thumb, use unstable cache.
Theres nothing inherently wrong with it, besides the fact that you shouldnt be using server actions for fetching data.
Champagne D’ArgentOP
I'm not, i just thought I was, got the wrong terminology
git blame = me
Oh, then yes use unstable cache
export const dynamic = 'force-dynamic'
it should cache it completely until revalidated on demand
I go now, your in good hands.
Champagne D’ArgentOP
Should force-dynamic do that?
or unstable cache 😄
Force dynamic is better option
Champagne D’ArgentOP
I'm using force-dynamic already, otherwise I got a build error
Because it can't statically generate a page in build-time that required a database call
which makes sense
but it doesn't look to be caching anything
Champagne D’ArgentOP
rn stable, trying out unstable_cache
const getCachedCurrentYear = unstable_cache(
async function getCurrentYear() {
const year = await database.getCurrentYear();
if (!year) {
redirect("/inactive");
}
return year;
},
["current-year"]
);
Seems to cache it 👍 Haven't tried invalidating, but I would assume that would work too...
Thanks a lot for the guidance!!
Edited the post for searchability