supabase-js
supabase-js copied to clipboard
Custom fetch per request
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.
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) });
I don't think this will impact Realtime.
There shouldn't be an issue with using multiple clients, so closing this.
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.
Oh man @5hee75 I was wondering forever why this happened in my app. This explains it perfectly.
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.
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.
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.
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.
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,
}
}
});