GuidesUse with React Query

Using with tanstack react-query

Tanstack Query is a popular data fetching library for react applications. We don’t offer any out the box integration with it (yet), but it does integrate easily with our generated client SDKs.

Here’s a basic example implementation. It’s not perfect, but it should give you a good starting point.

Define the queries and mutations

First we setup a context and define the queries / mutations we’ll be making

import {ApiClient} from "@/generated/clients/client"
import {t_ScanRepositoriesBodySchema} from "@/generated/models"
import {
  QueryClient,
  UseMutationOptions,
  queryOptions,
} from "@tanstack/react-query"
import {createContext, useContext} from "react"
 
 
export const QueryOptionsContext = createContext<
  ReturnType<typeof createQueryOptions> | null
>(null)
 
export const useQueryOptions = () => {
  const v = useContext(QueryOptionsContext)
 
  if (!v) throw new Error("useQueryOptions must be used within provider")
 
  return v
}
 
export const createQueryOptions = (
  fetchClient: ApiClient,
  queryClient: QueryClient,
) => {
  const result = {
    getSomeResource: (
      id: string,
    ) => {
      return queryOptions({
        queryKey: [
          "getSomeResource",
          id
        ],
        queryFn: async () => {
          const res = await fetchClient.getSomeResource({
            id,
          })
 
          if (res.status !== 200) {
            throw new Error("request failed", {
              cause: new Error(await res.text()),
            })
          }
 
          return await res.json()
        },
      })
    },
 
    updateSomeResource: (): UseMutationOptions<
      void,
      Error,
      t_ScanRepositoriesBodySchema
    > => {
      return {
        mutationKey: ["updateSomeResource"],
        mutationFn: async (id: string, body: t_SomeBodyType) => {
          const res = await fetchClient.updateSomeResource({id, requestBody: body})
 
          if (res.status !== 204) {
            throw new Error("request failed", {
              cause: new Error(await res.text()),
            })
          }
        },
        onSuccess: () => {
          void queryClient.invalidateQueries({queryKey: ["updateSomeResource"]})
        },
      }
    },
  }
 
  return result
}

Initialize the clients

At the root of our application provide the context

import {
  QueryOptionsContext,
  createQueryOptions,
} from "@/app/providers/query-options"
import {ApiClient} from "@/generated/clients/client"
import {QueryClient, QueryClientProvider} from "@tanstack/react-query"
import {ReactQueryDevtools} from "@tanstack/react-query-devtools"
import {ReactQueryStreamedHydration} from "@tanstack/react-query-next-experimental"
import * as React from "react"
import {useState} from "react"
 
export function ClientProviders(props: {children: React.ReactNode}) {
  const [queryClient] = useState(
    () =>
      new QueryClient({
        defaultOptions: {
          queries: {
            staleTime: 5_000,
          },
        },
      }),
  )
 
  const [fetchClient] = useState(
    new ApiClient({
      basePath: "http://localhost:3000",
    }),
  )
 
  return (
    <QueryClientProvider client={queryClient}>
      <QueryOptionsContext.Provider
        value={createQueryOptions(fetchClient, queryClient)}
      >
        <ReactQueryStreamedHydration>
          {props.children}
        </ReactQueryStreamedHydration>
        <ReactQueryDevtools initialIsOpen={false} />
      </QueryOptionsContext.Provider>
    </QueryClientProvider>
  )
}
 
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode
}>) {
  return (
    <html lang="en">
      <head>
        <meta name="viewport" content="initial-scale=1, width=device-width" />
      </head>
      <body>
          <ClientProviders>
                {children}
          </ClientProviders>
      </body>
    </html>
  )
}

Use the queries

Finally use the useQueryOptions hook in conjunction with the normal useQuery / useMutation hooks to make your requests

import {useQueryOptions} from "@/app/providers/query-options"
import {useMutation, useQuery} from "@tanstack/react-query"
 
export const SomeComponent = () => {
 
  const queryOptions = useQueryOptions()
 
  const someResource = useQuery(
    queryOptions.getSomeResource("123"),
  )
  const doUpdate = useMutation(queryOptions.updateSomeResource())
 
  return <div>
    <h1>{someResource.title}</h1>
    <button onClick={() => doUpdate.mutate("123", {title: "new title"})}/>
  </div>
}