Docs: Use Cache + Supabase
What is the documentation issue?
Could we have a full example where a database connection is needed, and also where we want to check if the user is really who he is?
At this stage everything i've saw is splitting the "currentUser" from the call to the database. However that is quite risky, as the security is completely bypassed by whoever know the serverAction to be called.
I may be not understanding the different "use cache", however the use cache: private seems the perfect case I need, but on server-side!
Example:
"use server";
import { cacheLife } from "next/cache";
import { createADMINClient } from "../tests-cache/database/utility";
export type AssistantRole = {
assistant_id: string;
role_name: string;
};
//Passing the accountId "separate" as useCache cannot get the "getCurrrentUser"
export async function getRoleName ( accountId: number ): Promise<AssistantRole[] | null>
{
"use cache";
cacheLife( "hours" );
console.log( 'cache' );
const supabase = createADMINClient();
const query = supabase
.from( "assistants" )
.select( "assistant_id, role_name" )
.eq( "account_id", accountId );
const { data, error } = await query;
if ( error )
{
console.error( error );
return null;
}
return data;
}
Calling function
const currentUser = await getCurrentUser();
if ( !currentUser?.account_id )
{
return null;
}
const roles = await getRoleName(currentUser.account_id);
How do I cache this function for an hour?
The console.log is getting cached most of the time right now, however if there is a save from the database the cache is getting refreshed... WHY?
Console.log
cache => Not cached
Cache cache => Cached
However if i go in a page that doesn't do updates, everything works fine.
Is there any context that might help us understand?
Does the docs page already exist? Please link to it.
No response
Ok it seems that revalidation is triggered by other tags not specified here: https://nextjs.org/docs/app/api-reference/directives/use-cache
Also, is there an easy way to see what has been written to the cache? It's extremely hard to understand how this thing is working behind the scene!
A simple cli command that showcase what and how has been saved would be a life-saver, so that I can understand how it is working behind the scene, and if is doing what I need.
- https://nextjs.org/docs/app/guides/incremental-static-regeneration#verifying-correct-production-behavior
NEXT_PRIVATE_DEBUG_CACHE=1 in an .env file or NEXT_PRIVATE_DEBUG_CACHE=1 pnpm dev, etc, would start to log cache activity, but also other ISR activity, it is a bit rough right now.
I am hesitant to link or reference that in the cache key section in the use cache docs, because it logs a lot more than just use cache information, but perhaps I just have to do it.
Also, if you do activate https://nextjs.org/docs/app/api-reference/config/next-config-js/logging then you should see replay logs from Cache right?
revalidation is triggered by other tags
What do you mean there?
And for the exposed server function, you should refactor to (as you say, it is not very good to expose that endpoint like that, it should have some checks) - at least I think that should do the trick no?
// not exported
async function getRoleNameFromClient(accountId: number) {
"use cache";
cacheLife( "hours" );
console.log( 'cache' );
const supabase = createADMINClient();
const query = supabase
.from( "assistants" )
.select( "assistant_id, role_name" )
.eq( "account_id", accountId );
const { data, error } = await query;
if ( error ) {
console.error( error );
return null;
}
return data;
}
export async function getRoleName(): Promise<AssistantRole[] | null> {
const currentUser = await getCurrentUser();
if ( !currentUser?.account_id ) {
return null;
}
const roles = await getRoleName(currentUser.account_id);
return roles;
}
@icyJoseph Mate, you're a legend! I didn't expect an answer that soon!
- Thanks for confirming my worries about exposing a non-safe function
- Yes having that command in the Cache documentation would be a life-saver
- The cache seems that is busted even when we use revalidatePath vs what we say here: https://nextjs.org/docs/app/getting-started/cache-components
question 1
I'm still puzzled why you've not used the "use cache: remote", theoretically that is a 'Dynamic Context', but i cannot find the definition of Dynamic Context (https://nextjs.org/docs/app/guides/caching#dynamic-rendering this??)
question 2
await connection() -> https://nextjs.org/docs/app/api-reference/functions/connection
I'm not getting how this work. . Theoretically any function that is calling the database and passing from the auth system it should be dyanmic as it needs header/cookies. But that do not seem the case as use cache is working in the function above.
question 3
https://nextjs.org/docs/app/api-reference/directives/use-cache-remote#basic-example
Could you explain what happen when we remove the await connection() here?
In my app, whenever i have a route like this and i never used await connection() i always get the freshest data. Am i unaware or something going behing the scene?
summary
Apologies but i'm so lost with all these caching mechanism. . I was get used to, give me a cache key, i'll store it for you and you can retrieve it whenever you like it 😆
I am doing a rework of our use cache remote story. My answers here might be a bit off. You may also refer to: https://github.com/vercel/next.js/issues/85240#issuecomment-3560124078
q1: What that version is trying to get you to do, is to use a remote cache, outside the static shell. use cache is a very first good primitive to use first. There's not that much you get out of using a remote cache in the static shell, besides some edge cases. The shell is the cache so to speak, what to send to users it is already saved to disk. And remote caching costs money.
For an edge case, imagine if you have deployed using multiple Next.js servers, each running its own Node.js process. As user requests arrive, you might need to revalidate some cache entry, say, translations from a CMS. With stock use cache, the cache exists in the process memory, so each instance will query for translations at least once. With a remote cache, you'd potentially query the CMS source only once (in a well implemented handler) during this revalidation "burst".
q2: if createADMINClient just does a class instantiation or whatver, synchronous work to setup how to reach for your DB, then there's nothing more cookies based here, you extracted the accountId from the cookies, and can now cache the getRoleNameFromClient as a function of the accountId input.
const supabase = createADMINClient();
const query = supabase
.from( "assistants" )
.select( "assistant_id, role_name" )
.eq( "account_id", accountId );
In this case, because in order to get there, you unwrapped cookies, you were not part of the static shell already, you could've been explicit and play it safe dropping a connection call somewhere in the line.
q3: then you'd include the query in the static shell, there's no cookies/headers/searchParam read on the path to the db query - this is like, why that version, pushes this idea of, making sure you deferred to request time
I'll get back to you about the revalidatePath behavior. It's just, in general, I am finding it more "ergonomic" to work with tags.
Ok your comment is much better than anything in the documentation at this stage!
FYI: "use cache" when used with a good amount of data, seems that is failing silenty. And I noticed thanks to your CLI command 👍 . Nonetheless in dev, i'm still struggling to understand what is being cached and what is not.... mostly when it comes to UI component, however i believe this is just the start of caching so many things are still under development.
the RevalidatePath is not something i want. . . it was a side effect as my cache got invalidated and that's why i was thinking it was not working as i set the expiration in an hour, however saving a value in the databasse and using the revalidatePath busted even the 'use cache'.
FYI: "use cache" when used with a good amount of data, seems that is failing silently.
It might be evicting entries. It uses a LRU in memory cache. Is that what you mean? Try setting cacheMaxMemorySize in Next Config. It defaults to 50mb. I want to have an independent docs page for it, but for now it is only mentioned here https://nextjs.org/docs/app/api-reference/config/next-config-js/incrementalCacheHandlerPath.
// default to 50MB limit
cacheMaxMemorySize: 50 * 1024 * 1024,
The cache entry is made up by the key, which is the build id, the closures and arguments of the function, plus HMR unique id for dev mode. The cache output is yeah the return of the function.
So in some cases, you might benefit from transforming your data before returning, like removing unused fields, filtering out entries, etc... to reduce the size of what gets stored.
And yes, thanks for the patience. As you can see, while some descriptions are technically correct, the teaching story might need to describe things differently, while still being correct. Finding that point takes time.
Mate you're doing a great job. Caching is hard full stop.
What is making it harder in this case is the unability to see what is cached and what is not... you can have route / function / UIs, and at this stage with the dev environment re-compiling i'm not sure what is working and what is not.... 😆
Also the "use cache" vs "use cache: remote" is not trivial, but i understand that is done for the interoperability across system (not everyone uses vercel).
Anyway thank you very much! I'll keep my eyes on it, for now i'm using it only for one functionality, and would it be great too ohave some sort of DX tool that in dev will say "hey this funcion hs been cached, same as this route, or this component"
Last one... i have a page that is (Static)
├ ○ /testimonials
in development adding the "use cache" seems like makes the rendering faster (as it was suppose to be)... however because it's statics, could you confirm that the caching is completely useless?