If you are using Supabase in React, it's a good idea to combine it with React Query. Let's first have a look at how you fetch data in Supabase.
const { data, error } = await supabase
.from('todo')
.select('id, name')
.eq('done', false);
This call queries the todo
table, selecting the id
and name
columns and filters for those to-dos that are not done
. If error
is not null, something went wrong, and data
is null
. Otherwise, data is an array of objects representing the rows that matched the query.
Since a potential error is returned, the promise we await here never rejects. But the query function we use in useQuery
is supposed to either resolve data or throw an error. If we simply wrap the query from above in a useQuery
call, React Query cannot detect if the call failed:
// ⛔️ silently swallows a potential 'error'
useQuery(
['open-todos'],
() => supabase
.from('todo')
.select('id, name')
.eq('done', false)
);
To write a useQuery
-conform query function, we can explicitly throw an error if one occurs:
useQuery(
['open-todos'],
async () => {
const { data, error } = await supabase
.from('todo')
.select('id, name')
.eq('done', false);
if (error) {
throw new Error(`${error.message}: ${error.details}`);
}
return data;
}
);
Since all calls to Supabase follow the same pattern, we can introduce a small function to reuse this explicit handling of errors:
function useOpenTodos() {
return useQuery(
['open-todos'],
() => supabase
.from('todo')
.select('id, name')
.eq('done', false)
.then(handleSupabaseError)
.then(({ data }) => data)
);
}
function handleSupabaseError({ error, ...rest }) {
if (error) {
throw error;
}
return rest;
}
This way, you don't have to write the same boilerplate for every supabase
call and can effectively make use of the benefits of React Query's data management.
Top comments (0)