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

Custom fetch per request

Open enyo opened this issue 2 years ago • 10 comments

Feature request

When using the load function in Svelte Kit, it provides a fetch implementation that can be used.

From the official docs:

SvelteKit's load receives an implementation of fetch, which has the following special properties:

  • it has access to cookies on the server
  • it can make requests against the app's own endpoints without issuing an HTTP call
  • it makes a copy of the response when you use it, and then sends it embedded in the initial page load for hydration

Describe the solution you'd like

I think that it would be necessary to specify the fetch on a "per request" basis. Something like this:

supabase.withFetch(fetch).from('mytable').select();

Full example:

<script context="module">
  export const prerender = true;
  import { supabase } from "$lib/initSupabase";

  export async function load({ params, fetch, session, stuff }) {
    let { data, error } = await supabase.from("users").select();

    if (error) {
      return {
        status: 500,
      };
    } else {
      return {
        props: {
          users: data,
        },
      };
    }
  }
</script>

Describe alternatives you've considered

Maybe I'm overthinking this. The way I've solved this so far is by creating a function:

export const getSupabase = (fetch) => createClient(url, anonKey, { fetch });

I then use it in my load function.

Is there a downside in creating multiple clients like this? If not, this issue can probably be closed.

enyo avatar May 06 '22 10:05 enyo

Not sure about Auth and Realtime, but for REST this should be fine.

Alternatively (and this is a bit hacky):

// initSupabase.js
let currentFetch;

export function setFetch(fetch) { // call this per request
  currentFetch = fetch;
}

export const supabase = createClient(..., { fetch: (...args) => currentFetch(...args) });

soedirgo avatar May 09 '22 05:05 soedirgo

I don't think this will impact Realtime.

w3b6x9 avatar Aug 03 '22 21:08 w3b6x9

There shouldn't be an issue with using multiple clients, so closing this.

soedirgo avatar Sep 28 '22 08:09 soedirgo

There shouldn't be an issue with using multiple clients, so closing this.

@soedirgo I have definitely encountered an issue with multiple clients, specifically when using auth listeners. I created a client factory, similar to OP, where each request to the SDK would create a new client. I did this so I could inject dynamic headers and settings into the client for each execution.

const supa = (...params) => createClient(...params);

supa().from...
supa().auth...
// etc

The issue I found was that auth listeners/events are registered to a specific client instance, not to the auth events themselves. I'm not sure if this is intended or not.

So in my example above, if I do this:

const supa = () => createClient(...);

supa().auth.onAuthStateChange((event, session) => {
  if (event === 'SIGNED_OUT') {
    console.log('Auth Event', event);
  }
});

supa().auth.signOut();

The listener is never called, presumably because I signed out from a different client instance. The auth token and user details are still removed from localStorage though.

5hee75 avatar Oct 11 '22 05:10 5hee75

Oh man @5hee75 I was wondering forever why this happened in my app. This explains it perfectly.

enyo avatar Oct 11 '22 08:10 enyo

I'm doing a lot of SvelteKit these days and I'd definitely like to see the ability to specify the fetch to use per request. Although everything works in my project, I'm not happy seeing the message from svelte about using window.fetch rather then their version. And I'm not convinced that creating a new database connection for each query is a good thing.

johndeighan avatar Feb 02 '23 03:02 johndeighan

I haven’t yet found out why exactly, but I upgraded svelte kit + supabase, and now using a new supabase instance for an individual route breaks the routing completely. The page just never stops loading and there is no error. It’s very strange but it has definitely something to do with using a different supabase instance because if I just use the global instance it works fine.

EDIT: I realised that even just calling createClient() will break page rendering, even if then the singleton supabase instance is used. I assume that this has something to do with the auth state being broken.

enyo avatar Feb 22 '23 10:02 enyo

Not sure about Auth and Realtime, but for REST this should be fine.

Alternatively (and this is a bit hacky):

// initSupabase.js
let currentFetch;

export function setFetch(fetch) { // call this per request
  currentFetch = fetch;
}

export const supabase = createClient(..., { fetch: (...args) => currentFetch(...args) });

@soedirgo This would be an acceptable solution but it runs into race conditions. Especially on the server during SSR this might mix up two requests.

enyo avatar Feb 22 '23 11:02 enyo

This would be a great addition. I like to use the handleFetch hook from SvelteKit to log all fetches but for that to work Supabase would have to use the SvelteKit provided fetch function.

MLNW avatar May 19 '23 21:05 MLNW

This can also be done when creating the client in hooks.server.ts, e.g.

    event.locals.sb = createSupabaseServerClient<Database>({
        supabaseUrl: PUBLIC_SUPABASE_URL,
        supabaseKey: PUBLIC_SUPABASE_ANON_KEY,
        event,
        options: {
            global: {
                fetch: event.fetch,
            }
        }
    });

ConProgramming avatar Feb 26 '24 22:02 ConProgramming