Gauntlet DesignsArticlesAI Solutions

Wrapping Supabase Library With Tanstack

The Supabase client library is great for interacting with Supabase, but it leaves it on you, the developer, to handle state management and refetching. A commonly used tool to handle this is react-query by Tanstack.

Wrapping a Simple Get Request

Making a simple request via the Supabase API uses the following code.

const { data } = await supabase
  .from('users')
  .select('*')
  .throwOnError()

However, using this, we must manually re-call the function and update our data whenever we think the data may have changed.

Instead we can use Tanstack to solve this.

import {useQuery} from '@tanstack/react-query'

export const useGetUsers = () => {
  const supabase = useSupabaseClient();
  return useQuery({
    queryKey: ['users'],
    queryFn: async () => {
      const {data} = await supabase
        .from('users')
        .select('*')
        .throwOnError()
      return data || []
    }
  })
}

We can use this react hook by accessing the portions we need of the useGetUsers function.

const {data: users, isLoading} = useGetUsers();

WIthin this component, you now have a loading variable "isLoading" and the list of users will be stored in "users".

Refetching

Tanstack, by default, will automatically refetch the list of users by re-running the queryFn whenever the data is considered stale.

In the event that you do not want the auto refetching, my preference is to disable the query, and then call the refetch function when I need the call to happen.

...
  return useQuery({
    queryKey: ['users'],
    enabled: false,
    queryFn: async () => {
...
const {data: users, refetch, isLoading} = useGetUsers();

Get Requests With Dynamic Props

The last major use case that we run into is fetching data based on a dynamic value. For instance, if we want to get the user's data for the user profile page /user/1. We can accomplish this by enabling the query fn only when the user id is truthy. This will prevent a race condition for firing a request looking for user /user/undefined.

export const useGetUser = (user_id: string) => {
  const supabase = useSupabaseClient();
  return useQuery({
    queryKey: ['users', user_id],
    enabled: !!user_id,
    queryFn: async () => {
      const {data} = await supabase
        .from('users')
        .select('*')
        .eq('id', user_id)
        .single()
        .throwOnError()
      return data || []
    }
  })

Here we have a few changes. We have added a new item to our list of queryKey's. This will allow us to mark this query as stale easily from anywhere within the app. We cast the user_id to a boolean, and use that to determine if the query should be enabled or not. Lastly we modified the queryFn to include the ability to select the single user's data.

Gauntlet Designs
Contact us
contact@gauntletdesigns.com
Gauntlet Designs