Skip to content

React QueryReact Query Setup

React Query Setup

Integrate Courier with @tanstack/react-query for automatic caching, refetching, and type-safe hooks.

2 min readproxy mode

Overview

createProxyQueryCaller creates a type-safe query client with dot-notation access to all your endpoint groups. Each group exposes useQuery, useMutation, useSuspenseQuery, useInfiniteQuery, and prefetchQuery.

Hooks exist per group only: endpoints declared at the top level of defineApi({ endpoints }) have none, so declare the endpoints your components use inside a group.

Step 1: Install @tanstack/react-query

React Query is a peer dependency, version 5.80 or later — install it alongside Courier:

npm install @tanstack/react-query

Step 2: Set up QueryClientProvider

React Query requires a QueryClientProvider at the root of your app. Create a client component that wraps your layout:

app/providers.tsx
// app/providers.tsx
"use client"

import { QueryClient, QueryClientProvider } from "@noverax/courier-react"
import { useState, type ReactNode } from "react"

export function Providers({ children }: { children: ReactNode }) {
  const [queryClient] = useState(
    () =>
      new QueryClient({
        defaultOptions: {
          queries: {
            staleTime: 60 * 1000,
            retry: 1,
          },
        },
      })
  )

  return (
    <QueryClientProvider client={queryClient}>
      {children}
    </QueryClientProvider>
  )
}
app/layout.tsx
// app/layout.tsx
import { Providers } from "./providers"

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  )
}

Step 3: Create the Courier query caller

Call createProxyQueryCaller with your API type and a source name. Export the query client from a single file so every component imports from the same place:

lib/query.ts
// lib/query.ts
import { createProxyQueryCaller } from "@noverax/courier-react"
import type { api } from "@/api"

export const q = createProxyQueryCaller<typeof api>("api")

Then use it in your components with dot-notation:

components/user-list.tsx
"use client"

import { q } from "@/lib/query"

export function UserList() {
  const { data, isLoading } = q.users.useQuery("getAll")
  // ...
}

Single export file

Keep the query caller in one file. This ensures all components share the same client instance and defaults.

Configuring defaults

createProxyQueryCaller accepts an optional defaults object to configure the underlying proxy caller. These defaults apply to every request:

lib/query.ts
// lib/query.ts
import { createProxyQueryCaller } from "@noverax/courier-react"
import type { api } from "@/api"

export const q = createProxyQueryCaller<typeof api>("api", {
  // Custom proxy route URL (default: "/api/request")
  proxyPath: { http: "/api/my-proxy" },

  // Request timeout in milliseconds
  timeout: 10000,

  // Include cookies in proxy requests
  credentials: "include",

  // Default headers for all requests
  headers: {
    "X-App-Version": "1.0.0",
  },

  // Global error handler
  onError: async (error) => {
    if (error.status === 401) {
      window.location.href = "/login"
    }
  },

  // Global success handler
  onSuccess: (data) => {
    console.log("Request succeeded:", data)
  },
})

Next steps

  • useQuery — fetch data with type-safe queries
  • useMutation — create, update, and delete with typed mutations
  • Proxy Caller — the underlying proxy caller API