Cache Revalidation Options #78513
Replies: 1 comment
-
Short answer Minimal setupTag your cached fetches: // any Server Component / Route Handler
const res = await fetch('https://api.example.com/posts', {
next: { tags: ['posts'] }, // cache tag
// cache: 'force-cache' // default on the server
})
const posts = await res.json() Invalidate on demand (webhook, Server Action, or Route Handler): // app/api/revalidate/posts/route.ts
import { revalidateTag } from 'next/cache'
export async function POST() {
revalidateTag('posts') // mark as stale
return new Response('ok')
} Behavior: Optional: background warm-upIf you want to avoid a user paying the first refetch cost, you can pre-warm after invalidating: export async function POST() {
revalidateTag('posts')
// fire-and-forget warm-up (no await)
fetch('https://your-site.com/blog', { cache: 'no-store' }).catch(() => {})
return new Response('ok')
} Or call the same data endpoint that your page uses to ensure the tag is re-populated. Path vs tag
You can combine both if needed (e.g., mutate → With
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
We need to be able to optionally expire/revalidate tags without immediately removing the stale responses, just like how time-based revalidation works.
Even with the canary
dynamicIO
/"use cache"
api, I do not see a way to implement stale-while-revalidate caching using tags. CurrentlyrevalidateTag(tag)
/unstable_expireTag(...tags)
completely purges the cache of entries that have those tags. These functions should optionally integrate with staleness configuration and optionally override as well, so that the cached functions may return stale results while they revalidate in the background.Here's an example of something simply not possible to implement in a way that integrates properly with the data cache. It's simplified but directly mirrors how a Sanity CMS query function that integrates with their live content api has to work:
The attempted behavior is to allow stale responses forever, and only mark responses as stale when I call
expireTag()
later on. The first thing to notice is that the tags can only be marked after the request is made, which is only possible with"use cache"
+cacheTag()
on canary. Even this canary api does not respect the infinite stale cache life. Once I callexpireTag()
, all pages with the affected queries block until fresh responses return. Another thing to note is that I have to callexpireTag()
in a route handler because there is no control of whether or not calling it within a server action can skip a router refresh. Ideally, the router would only refresh if the client-side cache was affected by the invalidated tags and those affected cached functions fully revalidated. The only work-around available is to use a route handler to sneak around Next's cache tracking.My opinion right now is that the ideal behavior takes this even further and allows for any page to optionally have an instant stale response from the full route cache that seamlessly reconciles via RSC / router refresh once background revalidation completes for each expired cached function. For revalidated data used in suspense boundaries, it would be ideal to optionally have the old data available for the placeholder (as a parameter to a placeholder component) and have the fresh version come in via the normal html stream or possibly be awaited for fully static rendering.
Beta Was this translation helpful? Give feedback.
All reactions