How the pieces connect
Supabase gives you a supabase-js client that builds queries against PostgREST (supabase.from('table').select()), plus separate Auth, Storage, and Realtime modules. React Query sits one layer above as the cache and request coordinator. It never talks to Postgres directly — it wraps the promise the Supabase client returns.
The pattern is to make each useQuery queryFn return the awaited Supabase call, then throw on error so React Query treats it as a failed query rather than caching a bad payload:
useQuery({
queryKey: ['rooms', filters],
queryFn: async () => {
const { data, error } = await supabase
.from('rooms').select('*').eq('status', filters.status)
if (error) throw error
return data
},
})
React Query owns caching, retries, and staleTime; Supabase owns the data and row-level security.