Skip to content

Query Options

queryOptions() generates TanStack Query options objects from your Eden Treaty routes.

Basic Usage

tsx
import { useQuery } from '@tanstack/react-query'
import { useTreaty } from './lib/treaty'

function Users() {
  const treaty = useTreaty()

  const { data, isLoading, error } = useQuery(
    treaty.api.users.queryOptions()
  )

  if (isLoading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>

  return (
    <ul>
      {data?.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  )
}

With Route Parameters

For routes with parameters (like /api/users/:id):

tsx
function UserProfile({ userId }: { userId: string }) {
  const treaty = useTreaty()

  const { data } = useQuery(
    treaty.api.users({ id: userId }).queryOptions()
  )

  return <div>{data?.name}</div>
}

With Query Parameters

Pass query parameters in the first argument:

tsx
// Route: GET /api/users?role=admin&limit=10
const { data } = useQuery(
  treaty.api.users.queryOptions({
    query: {
      role: 'admin',
      limit: 10
    }
  })
)

Overriding Options

Pass TanStack Query options as the second argument:

tsx
const { data } = useQuery(
  treaty.api.users.queryOptions(
    { query: { status: 'active' } }, // Eden params
    {
      // TanStack Query options
      staleTime: 1000 * 60 * 5, // 5 minutes
      enabled: !!userId,
      refetchOnWindowFocus: false,
      select: (data) => data.filter(u => u.active)
    }
  )
)

Or without Eden params:

tsx
const { data } = useQuery(
  treaty.api.users.queryOptions(undefined, {
    refetchInterval: 5000 // Auto-refresh every 5s
  })
)

With Suspense

Works seamlessly with useSuspenseQuery:

tsx
import { useSuspenseQuery } from '@tanstack/react-query'
import { Suspense } from 'react'

function Users() {
  const treaty = useTreaty()

  // TypeScript knows data is never undefined
  const { data } = useSuspenseQuery(
    treaty.api.users.queryOptions()
  )

  return <div>{data.length} users</div>
}

// Wrap with Suspense boundary
function App() {
  return (
    <Suspense fallback={<Loading />}>
      <Users />
    </Suspense>
  )
}

Skip Token

Use the enabled option to conditionally disable queries:

tsx
function UserProfile({ userId }: { userId: string | null }) {
  const treaty = useTreaty()

  const { data } = useQuery(
    treaty.api.users({ id: userId ?? '' }).queryOptions(undefined, {
      enabled: !!userId
    })
  )

  return data ? <div>{data.name}</div> : null
}

Prefetching

Prefetch queries in loaders or event handlers:

tsx
const queryClient = useQueryClient()
const treaty = useTreaty()

// In a loader
async function loader() {
  await queryClient.prefetchQuery(
    treaty.api.users.queryOptions()
  )
}

// On hover
function UserLink({ userId }: { userId: string }) {
  const prefetch = () => {
    queryClient.prefetchQuery(
      treaty.api.users({ id: userId }).queryOptions()
    )
  }

  return (
    <Link to={`/users/${userId}`} onMouseEnter={prefetch}>
      View User
    </Link>
  )
}

Query Keys

Access query keys directly:

tsx
const treaty = useTreaty()

// Get the query key for a route
const key = treaty.api.users.queryKey()
// → ['api', 'users', 'get']

const keyWithParams = treaty.api.users({ id: '1' }).queryKey()
// → ['api', 'users', { id: '1' }, 'get']

Return Type

The generated options object includes:

ts
{
  queryKey: ['api', 'users', 'get'],     // Automatic hierarchical key
  queryFn: () => client.api.users.get(), // Bound to your treaty client
  // ... any overrides you pass
}

This is a standard TanStack Query options object that works with all Query features.

Released under the Apache 2.0 License.