Help with caching | Next.js 15 App Router
Answered
American black bear posted this in #help-forum
American black bearOP
I've read this [documentation for caching](https://nextjs.org/docs/app/building-your-application/caching), however it only shows how I should go about caching the data from native
What I am interested in is caching the data from ORMs like DrizzleORM, which don't have API for
I am confused by seeing some people use
Does anyone have any relevant documentation, articles, or other content on caching in Next.js v15.
What I am trying to achieve is:
1. Fetch data using my ORM db client in server action
2. Associate cache with a tag like
3. Revalidate the cache for tag when I do a
Any help is appreciated 🙏
fetch
requests.What I am interested in is caching the data from ORMs like DrizzleORM, which don't have API for
tags
, revalidate
, etc.I am confused by seeing some people use
import cache from "react"
and some people use "use cache"
on functions they want to cache, and it's a nightmare to understand what I am supposed to do.Does anyone have any relevant documentation, articles, or other content on caching in Next.js v15.
What I am trying to achieve is:
1. Fetch data using my ORM db client in server action
2. Associate cache with a tag like
user-${userId}-settings
3. Revalidate the cache for tag when I do a
updateUserSettings
, postUserSettings
, deleteUserSettings
Any help is appreciated 🙏
Answered by Roseate Spoonbill
For caching any data you have two mechanisms built-in to go with:
-
-
Of course, you can also use something on your own - redis, some fast kv etc. In next 13 I used to write my simple json cache by hand with revalidation etc. Nowadays I'd recommend going with one of the things above before implementing something custom.
-
use cache
- newer, easier to use cache mechanism in Next 15. Only available in canary releases - https://nextjs.org/docs/app/api-reference/directives/use-cache-
unstable_cache
- the same, but earlier approach to do custom data caching. It will be replaced at some point with use cache
, but for now you can use it as is. https://nextjs.org/docs/app/api-reference/functions/unstable_cacheOf course, you can also use something on your own - redis, some fast kv etc. In next 13 I used to write my simple json cache by hand with revalidation etc. Nowadays I'd recommend going with one of the things above before implementing something custom.
38 Replies
Roseate Spoonbill
For caching any data you have two mechanisms built-in to go with:
-
-
Of course, you can also use something on your own - redis, some fast kv etc. In next 13 I used to write my simple json cache by hand with revalidation etc. Nowadays I'd recommend going with one of the things above before implementing something custom.
-
use cache
- newer, easier to use cache mechanism in Next 15. Only available in canary releases - https://nextjs.org/docs/app/api-reference/directives/use-cache-
unstable_cache
- the same, but earlier approach to do custom data caching. It will be replaced at some point with use cache
, but for now you can use it as is. https://nextjs.org/docs/app/api-reference/functions/unstable_cacheOf course, you can also use something on your own - redis, some fast kv etc. In next 13 I used to write my simple json cache by hand with revalidation etc. Nowadays I'd recommend going with one of the things above before implementing something custom.
Answer
American black bearOP
Thank you
I will use "use cache"
Roseate Spoonbill
Happy to help 🙂 Worth mentioning, that both of the above can be revalidated with
revalidateTags
.American black bearOP
@Roseate Spoonbill I have one more question regarding dynamicIO
cacheTag
utility.Can I use
cacheTag()
anywhere in the function where "use cache"
is used.Roseate Spoonbill
@American black bear yes, you can use it anywhere. Specifically, you can use queried data to create tags dynamically:
https://nextjs.org/docs/app/api-reference/functions/cacheTag#creating-tags-from-external-data
https://nextjs.org/docs/app/api-reference/functions/cacheTag#creating-tags-from-external-data
American black bearOP
Thanks
so this is okay even if it's in try/catch block
export async function getUserVehicles() {
"use cache";
try {
const user = await authorize();
cacheTag("user-vehicles", user.id);
const userVehicles = await getVehiclesFromUserId(user.id);
return responseSuccess(userVehicles);
} catch (error) {
return responseError(error);
}
}
Roseate Spoonbill
Yup, I would assume so.
American black bearOP
Don't think I understand this perfectly
Does this mean that when vehicles update for one user, I would have to revalidate it for all of them or am I missing something?
Roseate Spoonbill
Single tag is enough to revalidate the cache. If you need specific revalidation, create tag like
user-${user.id}
Also, you can only revalidate single tag
American black bearOP
makes sense
Roseate Spoonbill
I commonly use paterns like
to have an option of revalidating either entire collection, or a single entity
`"users", `user-${user.id}`
to have an option of revalidating either entire collection, or a single entity
American black bearOP
Using the
"use cache"
for some reason breaks my auth.I've tried wrapping my pages in suspense, like it says in the docs but that does not work.
I think it has something to do with using cookies in a page that uses caching.
Oh yea when you use “use cache” you need to mark the page to be statically generated at built time or wrap inside <Suspense> whatever component/page that’s calling the function
Roseate Spoonbill
An easy fix is to add
Suspense
to root layout. Note, that suspense must wrap entire component that uses async data. Putting Suspense in the page on the top won't work if that page acessess awaited data. Under the link that the error provided you have a couple examples that suggest more targetted aproach (including search params etc) - https://nextjs.org/docs/messages/next-prerender-missing-suspense#params-and-searchparamsAmerican black bearOP
Am I doing the caching on my functions wrong? They are returning undefined when I use
"use cache"
but work normally without it.Roseate Spoonbill
it's hard to tell to be honest. I don't know what authorization does on your end, or what are side-effects. Is it possible you cached a response without session/without user profile?
American black bearOP
okay,
Route /dashboard used "cookies" inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use "cookies" outside of the cached function and pass the required dynamic data in as an argument.
authorize
throws this error:Route /dashboard used "cookies" inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use "cookies" outside of the cached function and pass the required dynamic data in as an argument.
This is much harder to implement than with something like Redis...
I might just switch, the DX is awful
Roseate Spoonbill
yeah, it does make sense. Sorry to put you on this path. Honestly in something like your code, I'd extract
authorize
call outside of the cached request and put only user profile stuff in the cacheAmerican black bearOP
So basically caching the utility functions
getUserProfile, updateUserProfile, createUserProfile, etc. (revalidating in POST functions and caching in GET)
not the server actions themselves
which have authorization checking
Roseate Spoonbill
yep. Auth is not something that should be included in cache anyway. Otherwise you risk returning something like user data to someone who shouldn't see it (e.g. when cache tag is set with
undefined
as id due to code error).@American black bear I might just switch, the DX is awful
It’s an experimental feature not supposed to be used in production code yet
“use cache” works fine in a data layer where all you’re doing it fetching the actual data not requesting the user info/permissions to perform that fetch
First check if the user is authorized then call the cached function, those two things are different and need to be kept separated.
1- Fetch the user info (dynamic every time) and redirect/throw if now authorized
2- Fetch the data (could be cached, here’s where you’d put “use cache”)
1- Fetch the user info (dynamic every time) and redirect/throw if now authorized
2- Fetch the data (could be cached, here’s where you’d put “use cache”)
American black bearOP
After some work I think I got it working.
Thank you guys!
Roseate Spoonbill
Happy to help. Feel free to share some screenshots of succesfully deployed app in #nextjs-showcase or other related channel. I do not work for vercel, but I'm curious how it will end up.