React cache() vs Cache Components for user-specific data in multi-tenant SaaS
Unanswered
rob posted this in #help-forum
robOP
I'm building a multi-tenant property management SaaS (Next.js 15 App Router) and have questions about
caching patterns for user-specific data
caching patterns for user-specific data
6 Replies
robOP
1. Auth caching - Using React cache() for request-scoped deduplication:
This eliminates duplicate auth API calls when multiple server actions run in same request.
// lib/auth.ts
import { cache } from 'react'
export const getCachedUser = cache(async () => { const { user } = await supabase.auth.getUser()
return { user, error }
})This eliminates duplicate auth API calls when multiple server actions run in same request.
Subscription tier caching - Using module-level Map with 5-min TTL:
// lib/subscription.ts
const subscriptionCache = new Map<userId, { tier, expiresAt }>()
export async function getUserTier() {
const cached = subscriptionCache.get(userId)
if (cached && !expired) return cached.tier
// else fetch from DB and cache
}This reduces DB queries for subscription checks across pages.
Questions:
1. Is React cache() the right choice for user auth? Docs say "don't cache user-specific data with 'use cache'" -
does that warning apply to React's cache() too, or only Next.js Cache Components?
2. For subscription data (user-specific but changes rarely), should I:
* Use React cache() (re-query DB every request)
* Keep my current Map approach (5-min TTL, keyed by user ID)
* Use Redis/Upstash for proper cross-request caching
* Something else?
3 . Is there any risk of cross-user data leakage with module-level Map in serverless (Vercel)? Cache is keyed by userId but persists across warm container reuse.
I've read the caching docs but they don't cover multi-tenant user-specific patterns in depth. Any guidance appreciated, thanks
1. Is React cache() the right choice for user auth? Docs say "don't cache user-specific data with 'use cache'" -
does that warning apply to React's cache() too, or only Next.js Cache Components?
2. For subscription data (user-specific but changes rarely), should I:
* Use React cache() (re-query DB every request)
* Keep my current Map approach (5-min TTL, keyed by user ID)
* Use Redis/Upstash for proper cross-request caching
* Something else?
3 . Is there any risk of cross-user data leakage with module-level Map in serverless (Vercel)? Cache is keyed by userId but persists across warm container reuse.
I've read the caching docs but they don't cover multi-tenant user-specific patterns in depth. Any guidance appreciated, thanks
@rob Questions:
1. Is React cache() the right choice for user auth? Docs say "don't cache user-specific data with 'use cache'" -
does that warning apply to React's cache() too, or only Next.js Cache Components?
2. For subscription data (user-specific but changes rarely), should I:
* Use React cache() (re-query DB every request)
* Keep my current Map approach (5-min TTL, keyed by user ID)
* Use Redis/Upstash for proper cross-request caching
* Something else?
3 . Is there any risk of cross-user data leakage with module-level Map in serverless (Vercel)? Cache is keyed by userId but persists across warm container reuse.
I've read the caching docs but they don't cover multi-tenant user-specific patterns in depth. Any guidance appreciated, thanks
Is React cache() the right choice for user auth? Docs say "don't cache user-specific data with 'use cache'" -
does that warning apply to React's cache() too, or only Next.js Cache Components?
Only Cache Components.
React's cache has nothing to do with "use cache"
React's cache is just to deduplicate multiple calls in a single request context. so it is 100% safe to use for auth.
For subscription data (user-specific but changes rarely), should I:
Use React cache() (re-query DB every request)
Keep my current Map approach (5-min TTL, keyed by user ID)
Use Redis/Upstash for proper cross-request caching
Something else?
I am not well versed with multi-tenant apps but by the look of things, here is the rule of thumb:
one - do React.cache on operations that are heavy to repeat, ideally like decoding JWT for Auth thats why its perfect to be used with React.cache.
But you can also wrap React.cache for db access if it gets repeated multiple times in a single query like getUser() or getConfig()
two - "use cache" is designed to be synonymous to redis cache. it is persistent data caching made easily available in the Next.js API. It is not a redis based cache but a simple key-value cache that is persisted in between requests.
your "use cache" should not contain user-specific data and is preferable to do auth outside of the "use cache" either before or after without performance degradation.
usually using "use cache" in place of Redis/Upstash is sufficient unless you want to decouple caching or have more caching control that what is offered by "use cache". Additionally you can also integrate next's cache system with redis using
next.config.ts but im not sure if it can be elegantly implemented.Is there any risk of cross-user data leakage with module-level Map in serverless (Vercel)? Cache is keyed by userId but persists across warm container reuse.
If you correctly identified all the keys required for the cache then no.
In the past you need to identify closures and dependencies of the cached data function manually via
unstable_cache but with the introduction of "use cache", all closures and necessary key parts are detected automatically in the build process.I.e
This risk cross-user contamination:
const userid = '123'
const getData = unstable_cache(() => {
return db.getPostByUser(userid)
})()and you have to do this:
const userid = '123'
const getData = unstable_cache((userid) => {
return db.getPostByUser(userid)
})(userid)or
const userid = '123'
const getData = unstable_cache(() => {
return db.getPostByUser(userid)
}, [userid])().
But in the case of "use cache", all closures like userid are identified automatically as keyparts so that they dont contaminate with other caches.
If you defined your function outside of all scopes (like, not using cache inside a Component) you should be fine
The people over at Next.js recommends "use cache" of components though i havent tested the feasability of it to existing architecture. but im pretty sure there are many in other platforms that have figured it out. and im also sure that the people and nextjs also has extensively tested about cross-contamining data caches since its uhh quite important in my opinion