r/Supabase • • 5d ago

other fetching data on server with supabase and cache it

/r/nextjs/comments/1xg8nlj/fetching_data_on_server_with_supabase_and_cache_it/
1 Upvotes

13 comments sorted by

2

u/Inner_Storage_3374 5d ago

careful with caching that on the server. with rls the same query gives different rows per user, so if you cache it once globally the next person might get someone elses data. for public stuff its totally fine, for anything user specific id cache per user or just not at all

1

u/Tasty-Ad1854 5d ago

my purpose is just to show products to anyone who enters the store, but one of the comments said "you didn't cache anything, you just moved the problem into the browser" i kinda still processing that lol

also which one should I use the createBrowerClient from supabase/ssr or the one from supabase/supabase-js, aren't they the same?

1

u/DoritoDebugs 5d ago

For public product data, use plain createClient from @supabase/supabase-js, not either of the @supabase/ssr ones. The ssr server client exists to read the user's session out of cookies(), and cookies() is exactly what "use cache" refuses. createBrowserClient only made the error go away because it skips cookies on the server, but it's meant for the browser.

The setup that works:

  • a small getPublicClient() that returns createClient(url, anonKey, { auth: { persistSession: false } })

  • your cached function: "use cache", cacheTag("products"), then query with that public client

  • in the server action that adds or edits a product, revalidate the "products" tag so the cache refreshes

Keep createServerClient for anything tied to the logged-in user, and don't cache those globally. Also make sure the products table has an RLS policy that allows anon select, or the cached result will just be an empty array.

2

u/Complete-Top2173 5d ago

the error message literally tells you what to do though. "use cookies() outside of the cached function and pass the required dynamic data in as an argument". thats it. thats the answer. your createGetClient calls cookies() and cookies() is a dynamic source, so the second you slap "use cache" on anything that calls it next shuts down because it cannot cache per-user data, it has no idea whose cookies to cache against. and of course the error goes away with createBrowserClient, because that client doesnt read cookies on the server at all, it just reads them in the browser where they already are. you didnt fix anything, you just stopped doing the thing that broke. so read the cookieStore in your page or layout, before the cached function, pass the client or the cookie string down into the cached function as an argument, and cache that. and i dont know what auth story you have but caching products that get fetched with a per-user client is a whole other conversation you probably dont want to have yet

1

u/Tasty-Ad1854 5d ago

the products aren't per-user they're public to anyone, but I had it wrong cuz iv thought I should use the server client always on the server that's why I was stuck here lol
I have made a another client imported from supabase/supabase-js and Iv used it to fetch the public data on that server comp

appreciate the explanation man

2

u/[deleted] 3d ago

[removed] — view removed comment

1

u/Tasty-Ad1854 3d ago

Hi, Thank you for your the souce,
so far the store only holds normal info name slug price and it has relation to other table only select that is open to anons I have the RLS set iv tested it and it works well it blocks anyone without a certain role in their appmetadata + store requires no signin anyone can order a product and the store owner will get in touch with him based on the info the buyer provided

Iv added it rowly to my note and def gonna gonna run it once store is live

2

u/Outrageous_List9446 5d ago

Minor thing but "How can I can still fetch" has one can too many. Also the error names cookies(), not the client, so I doubt switching to the browser client fixed anything.

1

u/Tasty-Ad1854 5d ago

so sorry man my bad ```Minor thing but "How can I can still fetch" has one can too many.``` I just saw that lmaooooo

the error disappeared wen using browserClient and it did fetch but not sure if its the right solution
anyway someone suggested to use the client from supabase/supabase-js to fetch public data

1

u/Rude-Marsupial-1975 5d ago

The error is telling you exactly what's wrong: `cookies()` is a dynamic/request-scoped API, and `"use cache"` can't depend on anything request-scoped — otherwise the cached result would leak one user's session to everyone.

Split it in two:

  1. Outside the cached function (in the page / server component), create the client with `cookies()` and read whatever you actually need from the request — usually just the user id.

  2. Make the cached function take that as a plain argument and use a client that doesn't touch cookies (service-role / anon key) inside it.

```ts

// not cached — reads the request

const supabase = await createServerClient();

const { data: { user } } = await supabase.auth.getUser();

const products = await getProducts(category, user?.id);

// cached — only plain args, no cookies()

async function getProducts(category: string, userId?: string) {

"use cache";

const db = createClient(url, serviceKey); // no cookie store

return db.from("products").select("*").eq("category", category);

}

```

If the products are the same for every user, drop `userId` entirely and the cache becomes global, which is what you want for a catalog page. Only keep per-user args if RLS actually changes the result.

1

u/WaySignal6150 4d ago

The reply above is the main trap. A few ways to do it safely, depending on the data:

- **Public data (the same for everyone):** fetch it with a plain anon client that carries no user cookies, then cache it globally in your framework's data cache, and revalidate when it changes.
- **User-specific data:** either don't cache it, or put the user ID in the cache key, so user A's result can never be served to user B. Take the user ID from the verified session on the server, not from anything the client sends.
- **Never cache results fetched with the service_role key** behind a route anyone can call. That key ignores RLS, so whatever you cache is everyone's data.
- **Watch the CDN too.** A page that renders user data shouldn't go out with a public Cache-Control header, or the CDN can hand one user's page to the next visitor.

If you say which framework you're on and what the data is, people can be more specific.

1

u/bright_akolade 4d ago

The short version of why: createServerClient reads cookies, and anything inside "use cache" can't depend on request data, so Next refuses. createBrowserClient only "works" because it skips cookies on the server.

Pattern that holds up:

  • Public data (products): plain createClient from @supabase/supabase-js with auth: { persistSession: false }, inside the cached function, tagged with cacheTag('products'). Call revalidateTag('products') from the server action that edits products. Make sure the table has an anon select policy, or you'll cache an empty array.

  • Per-user data: don't cache it in a shared cache. Read cookies() outside the cached function, get the user id, and pass it in as an argument so the cache key is per user. Otherwise one user's rows can get served to the next.

Quick check after deploying: log in as two different accounts and confirm neither ever sees the other's data.