DEV Community

Sebastian Ruhleder
Sebastian Ruhleder

Posted on

Using React Query with Supabase

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);
Enter fullscreen mode Exit fullscreen mode

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)
);
Enter fullscreen mode Exit fullscreen mode

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;
  }
);
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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.

Discussion (0)