Reputation: 51
The example I'm working on is quite simple. I have a very simple NextJS app (using app directory) that's connected to a Supabase database. This is my first attempt to use app directory-- I've used NextJS professionally before, but only the page router.
For a bit of context, my app allows users to create events and add guests to those events. All updates are done on client components, in a pretty standard way:
Reads are all done in async server components:
Here's the problem: even though I can see that the DB has all the correct rows, I get a stale version seemingly returned in the server components with the SELECT API. This causes the UI to present stale data and makes my app nearly unusable. I've tried logging the results that the API returns, and the log also shows stale data-- the same data that makes its way into the actual UI and client components.
Interestingly, I've noticed that if I update some code and trigger a module refresh, I start getting up-to-date data. I dug into NextJS caching, and saw that there's a ton of general caching-related confusion, and other people have seen this issue. However, I've attempted all of their workarounds, and I just can't avoid this caching. At this point, I'm not sure if it's just Next, or if Supabase is also doing something that's making the cache busting particularly challenging.
Here's what I've tried so far, as suggested in NextJS docs and in a GitHub issue:
export const revalidate = 0
and export const dynamic = 'force-dynamic'
to server components performing the SELECT requestsunstable_noStore()
to different root components, including the core server components that fetch the datacreateClient
call in Supabase to create a global fetch
override that's basically just the standard browser fetch with no additional caching magicNo matter what I do, my Next caching persists. If I restart my Next server, the cache is busted and I get the right data showing up.
Could anyone point out what I'm missing if there's an obvious solution for this?
Upvotes: 0
Views: 507
Reputation: 1
The issue you're encountering stems from a misunderstanding of how caching works in Next.js.
Once data is cached, it remains so until you explicitly trigger a revalidation of the cache.
Let me break it down into two simple steps:
With Supabase, you can implement this approach as follows:
Here’s an example that I use in my own project:
import type { Database } from "@/types/database";
import { createBrowserClient } from "@supabase/ssr";
import { unstable_noStore } from "next/cache";
const fetchWithTags = (
input: RequestInfo | URL,
init?: RequestInit,
tag?: string,
): Promise<Response> => {
return fetch(input, {
...init,
next: {
...(init?.next || {}),
tags: tag ? [tag] : [],
},
});
};
interface Props {
tag?: string;
noStore?: boolean;
}
export const createClient = (
{ tag, noStore }: Props = { tag: undefined, noStore: false },
) => {
if (noStore) {
unstable_noStore();
}
return createBrowserClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
global: {
fetch: (input: RequestInfo | URL, init?: RequestInit) =>
fetchWithTags(input, init, tag),
},
},
);
};
When creating the Supabase client, you specify the tag like this:
createClient({ tag: "main/hero" });
Then, after the data is modified, you can revalidate the tag in your route handler or wherever appropriate:
...
revalidateTag("main/hero");
...
This will ensure that Next.js fetches the latest data whenever there are changes.
Or if you really don't care about setting tags, just use noStore
option, so everything will be not cached.
Upvotes: 0