Next.js Discord

Discord Forum

Understanding next.js caching

Unanswered
Asian black bear posted this in #help-forum
Open in Discord
Avatar
Asian black bearOP
I am trying to understand how to correctly use the caching in next 13.

The way I currently see it there is a way to cache everything forever, nothing at all, or revalidate after a certain time.
There doesn't seem to be a option anymore to set your own cache headers and set a cache for a certain time.

In my use-case I would want to cache the results of a fetch in a sever component for a certain time and get a fresh response for the first request after the time runs out.
Using Incremental Static Regeneration it returns the stale result for the first request after the time ran out, causing that user to see an (eventually very) outdated page.

Example:
I have a page with data that generally updates every 5 minutes. During the 5 minutes I want to get a cached version of the fetch or the entire page. Using static regeneration, i face the issue that if no user calls the page for some time (lets say 30min), the first user that looks at the page and triggers the revalidate, receives a very outdated page.

I basically want to achieve what was previously done by using something like
res.setHeader('Cache-Control', public, max-age=300, s-maxage=200' in the getServerSideProps function.

Also: It seems that calling a page directly and navigating to it via a <Link> behaives differently and doesn't revalidate the page at all. Is this a intended behavior?

59 Replies

Avatar
Asian black bearOP
bump
Avatar
Philippine Crocodile
does export const revalidate = 0.2 work?
Avatar
Asian black bearOP
No, that wouldnt solve anything. A shorter revalidation time still has the same problems I descripted in the example.
If the page isn't visited for a longer time, the first person visiting it, gets a outdated page.
Also: You can't even set it below 1:
Image
Avatar
Asian black bearOP
bump
I can't image that there is no solution for this? For me this was always the default use case for pages with changing data?

The way I see I only have 3 solutions:
- Use no cache, causing performance issues and a lot of unnecessary fetch requests
- Don't use SSR and fetch everything client-side where I can set cache control headers, causing a worse user experience
- Going back to Next.js 12 or going back to the pages folder
Avatar
Asian black bearOP
bump
Avatar
DirtyCajunRice | AppDir
where are you setting revalidate exactly
Avatar
tafutada777
@Asian black bear out of curiosity, very outdated means the data cached at build time?
or the last fetched data?
Avatar
Asian black bearOP
Meaning the last fetched data.
Let's again say I have data that updates around every 5 min and therefore have a revalidation time of 5 minutes (300 seconds):

User 1 visits the page and gets the current data.
User 2 visits the page 2 minutes later and gets the cached data (which is ok)
User 3 visits the page 50 minutes later and triggers the revalidate, but still gets the data from 52 minutes ago (which is very outdated)
in the page.tsx with export const revalidate = 60, but I also tried setting it in the fetch, which obviously results in the same outcome
Avatar
tafutada777
yeah, i know. it's like Stale-While-Revalidate in workbox. unfortunately, no way to garbage collect cache data in a certain period of time. or you would poll the endpoint periodically to keep it fresh.
Avatar
Asian black bearOP
Bump 😦
Avatar
Asian black bearOP
Bump 😦
Avatar
Asian black bearOP
Bump
Avatar
Asian black bearOP
Bump
Avatar
Asian black bearOP
Bump
Avatar
Asian black bearOP
Bump
Avatar
Asian black bearOP
Bump
Avatar
Asian black bearOP
Bump
Avatar
Saltwater Crocodile
I'm not familiar with Next.js but am looking at some infrastructure stuff. To be honest I'm not super comfortable with how much Next.js is taking on as a responsibility. Maybe there is nothing that can be done about this, but I'd rather have my caching system on top of Next.js. I'd like to have hooks I suppose to be able to deeper integrate caching with more granular fetches (e.g. a fully rendered page vs something more granular) in Next.js, but I think the default behavior should be "you do your own caching".

I might be totally off the mark though.

(free bump at least ;))
Avatar
Asian black bearOP
Bump
Avatar
Asian black bearOP
Bump
Avatar
Rafael Almeida
if the behavior you need isn't available by the caching methods of the new fetch, you can change it to never cache anything and store the data in an external service (like upstash or your own) to cache it instead, where you have more control
I am not sure there is a way to set the Cache-Control header from a page like you could do with getServerSideProps since the new headers function is read only, but I am pretty sure you can still do that in the middleware so if you just want browser cache you might want to give it a try
Avatar
Barbary Lion
i came across a recent PR for updating the docs for explaining how caching works. maybe there's something here that is relevant? https://github.com/vercel/next.js/blob/canary/docs/02-app/01-building-your-application/04-caching/index.mdx#L11
Avatar
Asian black bearOP
While it's very cool that this is now nicely documented, it just sums up the things I already know/tried and doesn't provide a solution to the use case I described in my first message. Basically, I either want to set the cache headers myself or use the Data Cache, but with a more aggressive revalidation approach. Meaning it should already be served to the first request coming in after the revalidation time has passed.

Feel free to correct me if I missed something, though.
That's kinda the way I am currently forced to to this, but it's just so weird to me that Next.js removed the, in my opinion the best and most obvious way,to handle caching (setting cache headers).
And then try to present Caching as a cool feature which the app router re-invented and can perfectly manage.

Like I can't repeat myself enough. This usecase is not something thats extremely rare. It's the default.
Frequently changing data in detail pages (dynamic urls) that should be cached for a certain amount of time.
Avatar
Asian black bearOP
Bump
Avatar
Alfonsus Ardani
What was your question?
Avatar
Asian black bearOP
Basically I want a cache that does the following:

- Cache stuff for a certain time
- If a request comes in after that time, fetch fresh data and cache that for the defined time. It's important, that the first request after the cache time gets fresh data otherwise if a page isnt visited for a longer time, the first user gets extremely outdated data.

or a way to let me set my own cache headers.
Avatar
Alfonsus Ardani
I dont think thats possible with the current state of persistent data cache.
I mean natively
Image
this diagram explains how the data cahing works in next.js
Avatar
Asian black bearOP
Yeah, seems that way...
Thanks for the help though. Sad that there is no solution for this case, as I think this is quite a common use case.
Avatar
Alfonsus Ardani
you could try bring it up in the next.js github issue
Avatar
Asian black bearOP
Avatar
Alfonsus Ardani
frankly speaking, the decision in the revalidation mechanism seemed kinda mysterious
Avatar
joulev & fuma pray for Schneider
don't use revalidation, but instead use a cron job to call revalidatePath()/revalidateTag() after that certain amount of time
it should do exactly as you want
Avatar
Tramp ant
by doing this you still get stale data on first request and revalidation is triggered on the background.

Ideally first request would not get stale data, show loading state while server re-renders
Avatar
joulev & fuma pray for Schneider
revalidation is triggered on the background
No, that’s wrong.
Image
Image
It always gets the newest data right after invalidation
On demand revalidation in the app router is not the same as in the pages router
Avatar
Tramp ant
not when working with ISR
the docs are a bit misleading in that sense. It works that way when working with SSG and SSR but ISR cache is not purged.
in my case its not terminal so I'm still using as is but its what the OP is talking about as well.

When using ISR, first request after any kind of invalidation returns stale data, second request returns regenerated page
Avatar
Rafael Almeida
this is why they suggested to use on-demand revalidation with a cronjob instead of revalidation, which doesn't have this issue
Avatar
Tramp ant
Have you guys tried it yourselves? Cron-job or manually triggered on-demand revalidation (revalidateTag, revalidatePath) still returns stale data on first request when using ISR
Avatar
Rafael Almeida
yeah I just tested it with my project
tbf I tried doing this a few weeks ago and it didn't work, but I just tried with latest canary and it is working
previously I had an API Route in the pages dir with await res.revalidate('/') because revalidatePath('/') wasn't working
Avatar
Tramp ant
thanks it did work! didnt realize there was a canary release yesterday.

quick question do you get 404 after on-demand revalidation, if you build and start server locally?

works fine on vercel though
Avatar
joulev & fuma pray for Schneider
Yes I have a prod app running this that’s why I could answer that confidently. I want to have background revalidation with on-demand revalidation but that’s not possible and the slow first post-revalidation request still bites me, but it’s exactly what you need that’s why I answered
Avatar
Southern rough shrimp
Can you summarise what you did? 😅
Avatar
Tramp ant
I set up a revalidate enpoint that when called will to call revalidatePath('path/i/want/to/revalidate')

in my case it didnt work until updating tonextjs 13.4.13