supabase-js icon indicating copy to clipboard operation
supabase-js copied to clipboard

`fetch` override does not go well with Next.js' caching

Open susemeee opened this issue 1 year ago • 8 comments

Bug report

  • [x] I confirm this is a bug with Supabase, not with my own application.
  • [x] I confirm I have searched the Docs, GitHub Discussions, and Discord.

Describe the bug

To assign cache tags to control Next.js' data caching, Supabase's custom fetch override should be used. Referred https://github.com/supabase/supabase-js/issues/725#issuecomment-1578811299

However there seems to be some incompatibility present; Next.js patched fetch API does not seem to be applied to Supabase's fetch, resulting data cache completely not to work.

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

There is a sample project for reproduction.

  1. Build and deploy sample project
    • A supabase project and deployment target should've set up; I used CloudFlare
  2. Data caching and API such as revalidateTag from Next.js does not work properly, compared to direct usage of fetch API in the test code

Expected behavior

Whether fetch is used directly or via Supabase API the data cache should be used.

Screenshots

If applicable, add screenshots to help explain your problem.

System information

  • Version of supabase-js: 2.38.4
  • Version of Node.js: v20.2.0
  • Version of Next.js: v13.5.4

Additional context

Add any other context about the problem here.

susemeee avatar Nov 22 '23 12:11 susemeee

I remember I had checked this in the past and it was using the correct fetch. We'll investigate. In the meantime, you can pass the fetch object to the createClient method: https://supabase.com/docs/reference/javascript/initializing?example=custom-fetch-implementation

thorwebdev avatar Dec 20 '23 07:12 thorwebdev

I can't reproduce this on the sample project with npm run dev.

and deployment target should've set up; I used CloudFlare

Can you clarify what this means? Are you deploying the Next.js project to CF Workers? I'm not familiar with Next.js-isms.

soedirgo avatar Dec 20 '23 08:12 soedirgo

Yeah, I used CF workers and pages for deployment. I wonder if the issue is specific to CF environment + Next.js edge runtime or not.

As a workaround I copy-pasted some of internal postgrest-js code (like this) and made supabaseNextCompatFetch wrapper function.

Passing global fetch object to the createClient method did not work but the method above did. Quite confusing.

susemeee avatar Dec 21 '23 06:12 susemeee

Not sure if this is related, but I have the opposite issue: I can't get rid of the cache...

hnykda avatar Dec 26 '23 16:12 hnykda

@susemeee Does your supabase client wrapper support setting tags on a query basis?

samuelbohl avatar Jan 23 '24 19:01 samuelbohl

I'm having the same issue on my project. Fetching data with the supabase server client causes next to not use the data cache at all. But if I manually create the url and use fetch directly then the cache works fine. @thorwebdev's solution didn't work.

fmorroni avatar Feb 25 '24 05:02 fmorroni

I'm having the same issue on my project. Fetching data with the supabase server client causes next to not use the data cache at all. But if I manually create the url and use fetch directly then the cache works fine. @thorwebdev's solution didn't work.

How did you write the custom fetch?

mickbut-ler avatar Jun 02 '24 05:06 mickbut-ler

this worked for me in dev mode, haven't tried in production on nextjs 14.x can cache supabase queries using tags.. it sets cache options per supabase-client and not per request. but better than nothing.


  export const createClientWithCache = ( cacheOptions: any = {} ) => {
    const cookieStore = cookies();

    return createServerClient(
      process.env.NEXT_PUBLIC_SUPABASE_URL!,
      process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
      {
        cookies: {
          getAll() {
            return cookieStore.getAll();
          },
          /*
          // got middleware for setAll..
          
          setAll(cookiesToSet) {
            try {
              cookiesToSet.forEach(({ name, value, options }) => {
                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.
            }
          },
          */
        },

        global: {
          fetch: (url: any, options = {}) => {
            return fetch( url, { ...options,  ...cacheOptions });
          }
        }
        
      },
      
    );
  };

  // usages:

  // import { createClientWithCache } from "@/utils/supabase/server";
  // ..
  // const supabase = createClientWithCache({
  //   cache: 'force-cache',   // or 'no-cache'  // didn't work without it
  //   next: {
  //     //revalidate: 30,
  //     tags: ['get-koi'],
  //   }
  // });
  //
  // const { data, error } = await supabase
  //   .from('kois')
  //   .select()
  //   .eq('id', id)
  // ..
  //
  // revalidateTag( 'get-koi' )

gukii avatar Aug 11 '24 03:08 gukii

I get the same error when running SSR in an angular environment.

YeongCheon avatar Oct 04 '24 15:10 YeongCheon