Next.js Discord

Discord Forum

Make data available globally across Server components

Unanswered
Yakutian Laika posted this in #help-forum
Open in Discord
Yakutian LaikaOP
Hey guys, I've been working on a Next.js + Supabase App. I have a nested layout, and in every single route (server component) I need to fetch the user. When loading the innermost child route, the user call is fired 3 times, which leads to long loading times. In the docs I've seen that fetch calls are automatically memoized, but it looks like this is not the case when using the supabase client. Is there a way to fetch the user once (maybe in the root layout), and to pass it to all the child routes?

I've also tried using providers, and this works, but obviously only for Client components. Is there a similar pattern for Server components?

47 Replies

Yakutian LaikaOP
Since I am using the supabase client, which is passed to my getUser function, the method is recomputed every time, since the reference changed, so this doesn't work. Ideally, I want to fetch the user in the layout, and the pass the data down the server components. Is there no other way to achieve this?
Yakutian LaikaOP
Yes. I need the properties like the userId in multiple sub routes, all of them are Server components
if you put the getUser function inside the react cache function, what will happen?
Yakutian LaikaOP
Is called every time, since this getUser function has one param, which is a supabase client object. Since this object is instantiated every time, the reference changes, and the cache will not work like expected
you said, that you call the getUser function only in server components. So the function will be handled serverside. So the supabase client can also be initialized inside the function. What do you think about that?
Yakutian LaikaOP
Actually, let me try that. Thanks in the meantime
@Yakutian Laika Actually, let me try that. Thanks in the meantime
sure thing. I like to create only one instance of my client (in my case db) and then I can just import it into my file and can use it wherever I want
of course for you it's not a postgres client. For you it's a supabase client ^^
Yakutian LaikaOP
Creating the supabase client only once works, but I have issues with the cached result of my getUser function. So when wrapping cache around it, it only gets called once when loading my nested layout, that's fine. But when refreshing the page with router.refresh, or invalidating the path after submitting a form to change the name of the user, the data is not updated since the result of the method is still cached
Yakutian LaikaOP
Sure, it's very simple:
const supabase = createServerComponentClient();

async function getUser(): Promise<User | null> {
    const { data } = await supabase.auth.getUser();
    return data.user;
}

export const cachedGetUser = cache(getUser);
The thing is I am currently migrating my project from SvelteKit to Next.js. In SvelteKit, I could simply fetch the user in the root layout, and access this data in all the other components. So I was wondering if something like that is also possible in Next.js, without having to worry about caching functions.
The problem is that you’re calling the getUser function multiple times, right? You could save the authenticated user in a session and get from there instead. Then you wouldn’t be fetching everytime, just reading from session
@Abno The problem is that you’re calling the getUser function multiple times, right? You could save the authenticated user in a session and get from there instead. Then you wouldn’t be fetching everytime, just reading from session
it would be the same problem. It's not the problem "I am called to much my database". It's more the problem on "I called my database, but the cache is not updating after router.refresh"
Oh, I see
Yakutian LaikaOP
Yeah, I added a console.log to my getUser function. But after calling router.refresh, I get the same old properties instead of the new ones (which have been stored in the db successfully)
The function is being called, though
I think it is because React.cache memoizes the function. In every example in React docs, the function always takes some param. I believe since no argument is being passed to cachedGetUser, it is still cached
That’s why wrapping the same function instance with cache will give you different caches
@Yakutian Laika The function is being called, though
when the function is called, (so the console.log get called) then it's more a problem of supabase, right?
@Abno I think it is because React.cache _memoizes_ the function. In every example in React docs, the function always takes some param. I believe since no argument is being passed to cachedGetUser, it is still cached
Turkish Van
Based on the explained behaviour, You are experiencing, I would not say it's a result of React cache function usage (as B33fb0n said).

If You eventually shared a bit more of Your code, we might be able to assist further.
@Abno Oh, I’m not op, just trying to help as well lol
Turkish Van
Oh, my bad. Sorry!
happens the best :fine:
@Turkish Van Oh, my bad. Sorry!
np boss
looks like an own function... 🤔 like created by him
yeah, since cache isn’t the issue, maybe that’s doing something that’s breaking the intended behavior
yea maybe... but we don't know when OP is not talking with us 🤷‍♂️
Yakutian LaikaOP
Sorry guys, was afk for some time.
I renamed the createClient function from utils/supabase/server.ts to createServerComponentClient in my project, but it is the same method as in this tutorial
export function createServerComponentClient() {
    const cookieStore = cookies();

    return createServerClient<Database>(
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
        {
            cookies: {
                get(name: string) {
                    return cookieStore.get(name)?.value;
                },
                set(name: string, value: string, options: CookieOptions) {
                    try {
                        cookieStore.set({ name, value, ...options });
                    } catch (error) {
                        // The `set` method was called from a Server Component.
                        // This can be ignored if you have middleware refreshing
                        // user sessions.
                    }
                },
                remove(name: string, options: CookieOptions) {
                    try {
                        cookieStore.set({ name, value: '', ...options });
                    } catch (error) {
                        // The `delete` method was called from a Server Component.
                        // This can be ignored if you have middleware refreshing
                        // user sessions.
                    }
                },
            },
        }
    );
}
Yakutian LaikaOP
Turns out the default Next.js is causing caching issues within the supabase client: https://github.com/orgs/supabase/discussions/20022
Looks like the combination of adding export const revalidate = 0; to my server component and using cache for my db calls is working... even though it's more complicated as I'd like it to be...
hm.. I guess then you can also add noStore() to the top and your should be fine as well 🤔
Yakutian LaikaOP
One question: By using export const revalidate = 0; in my layout, is it valid for all the subroutes?
I couldn't find anything related to this in the docs
it should be, yea
Yakutian LaikaOP
Thanks guys for your help 👍
Nice
@Yakutian Laika Thanks guys for your help 👍
sure thing. Please mark solution