# TanStack React Query

> The options and key factories on the trpc proxy, such as queryOptions, infiniteQueryOptions, mutationOptions, subscriptionOptions, queryKey, queryFilter and pathFilter, plus key prefixing and type inference

From https://trpc.io/docs/client/tanstack-react-query/usage

## Quick example query

```tsx no-check
import { useQuery } from '@tanstack/react-query';
import { useTRPC } from './trpc';

function Greeting() {
  const trpc = useTRPC();

  const greetingQuery = useQuery(trpc.greeting.queryOptions({ name: 'Jerry' }));

  // greetingQuery.data === 'Hello Jerry'
  return <p>{greetingQuery.data}</p>;
}
```

## Usage

The philosophy of this client is to provide thin and type-safe factories which work natively and type-safely with Tanstack React Query. This means just by following the autocompletes the client gives you, you can focus on building just with the knowledge the [TanStack React Query docs](https://tanstack.com/query/latest/docs/framework/react/overview) provide.

```tsx no-check
export default function Basics() {
  const trpc = useTRPC();
  const queryClient = useQueryClient();

  // Create QueryOptions which can be passed to query hooks
  const myQueryOptions = trpc.path.to.query.queryOptions({ /** inputs */ })
  const myQuery = useQuery(myQueryOptions)
  // or:
  // useSuspenseQuery(myQueryOptions)
  // useInfiniteQuery(myQueryOptions)

  // Create MutationOptions which can be passed to useMutation
  const myMutationOptions = trpc.path.to.mutation.mutationOptions()
  const myMutation = useMutation(myMutationOptions)

  // Create a QueryKey which can be used to manipulate many methods
  // on TanStack's QueryClient in a type-safe manner
  const myQueryKey = trpc.path.to.query.queryKey()

  const invalidateMyQueryKey = () => {
    queryClient.invalidateQueries({ queryKey: myQueryKey })
  }

  return (
    // Your app here
    null
  )
}
```

The `trpc` object is fully type-safe and will provide autocompletes for all the procedures in your `AppRouter`. At the end of the proxy, the following methods are available:

### `queryOptions` - querying data

Available for all query procedures. Provides a type-safe wrapper around [Tanstack's `queryOptions` function](https://tanstack.com/query/latest/docs/framework/react/reference/queryOptions). The first argument is the input for the procedure, and the second argument accepts any native Tanstack React Query options.

```ts no-check
const queryOptions = trpc.path.to.query.queryOptions(
  {
    /** input */
    id: 'foo',
  },
  {
    // Any Tanstack React Query options
    staleTime: 1000,
  },
);
```

You can additionally provide a `trpc` object to the `queryOptions` function to provide tRPC request options to the client.

```ts no-check
const queryOptions = trpc.path.to.query.queryOptions(
  {
    /** input */
    id: 'foo',
  },
  {
    trpc: {
      // Provide tRPC request options to the client
      context: {
        // see https://trpc.io/docs/client/links#managing-context
      },
    },
  },
);
```

If you want to disable a query in a type safe way, you can use `skipToken`:

```ts no-check
const query = useQuery(
  trpc.user.details.queryOptions(
    user?.id && project?.id
      ? {
          userId: user.id,
          projectId: project.id,
        }
      : skipToken,
    {
      staleTime: 1000,
    },
  ),
);
```

The result can be passed to `useQuery` or `useSuspenseQuery` hooks or query client methods like `fetchQuery`, `prefetchQuery`, `prefetchInfiniteQuery`, `invalidateQueries`, etc.

### `infiniteQueryOptions` - querying infinite data

Available for all query procedures that take a cursor input. Provides a type-safe wrapper around [Tanstack's `infiniteQueryOptions` function](https://tanstack.com/query/latest/docs/framework/react/reference/infiniteQueryOptions). The first argument is the input for the procedure, and the second argument accepts any native Tanstack React Query options.

```ts no-check
const infiniteQueryOptions = trpc.path.to.query.infiniteQueryOptions(
  {
    /** input */
  },
  {
    // Any Tanstack React Query options
    getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
  },
);
```

### `unstable_$prefetch`, `unstable_$ensure` and `unstable_$fromHandle` - passing queries as handles

On the server or in a route loader, `trpc.x.unstable_$prefetch(input)`, `await trpc.x.unstable_$ensure(input)` and `await trpc.x.unstable_$fetch(input)` start a query and return a serializable handle. Components read it with `trpc.unstable_$fromHandle(handle)`, which returns the same options as `trpc.x.queryOptions(input)`. Infinite queries have `unstable_$prefetchInfinite`, `unstable_$ensureInfinite` and `unstable_$fetchInfinite`, and their handles give the same options as `trpc.x.infiniteQueryOptions(input)`. Type props with `unstable_inferQueryHandle` and `unstable_inferInfiniteQueryHandle`.

- `unstable_$prefetch` doesn't wait. The reader shows a loading state until the data streams in: a `Suspense` fallback with `useSuspenseQuery`, or `status: 'pending'` with `useQuery`.
- `unstable_$ensure` waits until the data is in the cache, so the reader shows the data on its first render.
- Handles carry no data, whichever method made them. If the cache loses the data, for example after `queryClient.resetQueries()`, the reader loads it again, so give it a loading state either way. See [When the cache loses the data](https://trpc.io/docs/client/tanstack-react-query/handles#cache-loses-data).
- `unstable_$fetch` also waits, and refetches stale data before it resolves.
- Methods with a `$` are tRPC helpers built on TanStack Query. Each one maps to a single TanStack Query call: see [TanStack Query equivalents](https://trpc.io/docs/client/tanstack-react-query/handles#tanstack-equivalents).

> [!NOTE] Info
> We have prefixed the handle APIs with `unstable_` as they're new, but you're safe to use them! [Read more](https://trpc.io/docs/faq#unstable).


See [Prefetching with handles](https://trpc.io/docs/client/tanstack-react-query/handles).

### `queryKey` - getting the query key and performing operations on the query client

Available for all query procedures. Allows you to access the query key in a type-safe manner.

```ts no-check
const queryKey = trpc.path.to.query.queryKey();
```

Since Tanstack React Query uses fuzzy matching for query keys, you can also create a partial query key for any sub-path to match all queries belonging to a router:

```ts no-check
const queryKey = trpc.router.pathKey();
```

Or even the root path to match all tRPC queries:

```ts no-check
const queryKey = trpc.pathKey();
```

### `infiniteQueryKey` - getting the infinite query key

Available for all query procedures that take a cursor input. Allows you to access the query key for an infinite query in a type-safe manner.

```ts no-check
const infiniteQueryKey = trpc.path.to.query.infiniteQueryKey({
  /** input */
});
```

The result can be used with query client methods like `getQueryData`, `setQueryData`, `invalidateQueries`, etc.

```ts no-check
// Get cached data for an infinite query
const cachedData = queryClient.getQueryData(
  trpc.path.to.query.infiniteQueryKey({ cursor: 0 }),
);

// Set cached data for an infinite query
queryClient.setQueryData(
  trpc.path.to.query.infiniteQueryKey({ cursor: 0 }),
  (data) => {
    // Modify the data
    return data;
  },
);
```

### `queryFilter` - creating query filters

Available for all query procedures. Allows creating [query filters](https://tanstack.com/query/latest/docs/framework/react/guides/filters#query-filters) in a type-safe manner.

```ts no-check
const queryFilter = trpc.path.to.query.queryFilter(
  {
    /** input */
  },
  {
    // Any Tanstack React Query filter
    predicate: (query) => {
      return !!query.state.data;
    },
  },
);
```

Like with query keys, if you want to run a filter across a whole router you can use `pathFilter` to target any sub-path.

```ts no-check
const queryFilter = trpc.path.pathFilter({
  // Any Tanstack React Query filter
  predicate: (query) => {
    return !!query.state.data;
  },
});
```

Useful for creating filters that can be passed to client methods like `queryClient.invalidateQueries` etc.

### `infiniteQueryFilter` - creating infinite query filters

Available for all query procedures that take a cursor input. Allows creating [query filters](https://tanstack.com/query/latest/docs/framework/react/guides/filters#query-filters) for infinite queries in a type-safe manner.

```ts no-check
const infiniteQueryFilter = trpc.path.to.query.infiniteQueryFilter(
  {
    /** input */
  },
  {
    // Any Tanstack React Query filter
    predicate: (query) => {
      return !!query.state.data;
    },
  },
);
```

Useful for creating filters that can be passed to client methods like `queryClient.invalidateQueries` etc.

```ts no-check
await queryClient.invalidateQueries(
  trpc.path.to.query.infiniteQueryFilter(
    {},
    {
      predicate: (query) => {
        // Filter logic based on query state
        return query.state.status === 'success';
      },
    },
  ),
);
```

### `mutationOptions` - creating mutation options

Available for all mutation procedures. Provides a type-safe identity function for constructing options that can be passed to `useMutation`.

```ts no-check
const mutationOptions = trpc.path.to.mutation.mutationOptions({
  // Any Tanstack React Query options
  onSuccess: (data) => {
    // do something with the data
  },
});
```

### `mutationKey` - getting the mutation key

Available for all mutation procedures. Allows you to get the mutation key in a type-safe manner.

```ts no-check
const mutationKey = trpc.path.to.mutation.mutationKey();
```

### `subscriptionOptions` - creating subscription options

TanStack does not provide a subscription hook, so we continue to expose our own abstraction here which works with a [standard tRPC subscription setup](https://trpc.io/docs/server/subscriptions).
Available for all subscription procedures. Provides a type-safe identity function for constructing options that can be passed to `useSubscription`.
Note that you need to have either the [`httpSubscriptionLink`](https://trpc.io/docs/client/links/httpSubscriptionLink) or [`wsLink`](https://trpc.io/docs/client/links/wsLink) configured in your tRPC client to use subscriptions.

```tsx no-check
function SubscriptionExample() {
  const trpc = useTRPC();
  const subscription = useSubscription(
    trpc.path.to.subscription.subscriptionOptions(
      {
        /** input */
      },
      {
        enabled: true,
        onStarted: () => {
          // do something when the subscription is started
        },
        onData: (data) => {
          // you can handle the data here
        },
        onError: (error) => {
          // you can handle the error here
        },
        onComplete: () => {
          // the subscription was ended by the server, `status` is now `'idle'`
        },
        onConnectionStateChange: (state) => {
          // you can handle the connection state here
        },
      },
    ),
  );

  // Or you can handle the state here
  subscription.data; // The lastly received data
  subscription.error; // The lastly received error

  /**
   * The current status of the subscription.
   * Will be one of: `'idle'`, `'connecting'`, `'pending'`, or `'error'`.
   *
   * - `idle`: subscription is disabled or ended
   * - `connecting`: trying to establish a connection
   * - `pending`: connected to the server, receiving data
   * - `error`: an error occurred and the subscription is stopped
   */
  subscription.status;

  // Reset the subscription (if you have an error etc)
  subscription.reset();

  return <>{/* ... */}</>;
}
```

### Query Key Prefixing

When using multiple tRPC providers in a single application (e.g., connecting to different backend services), queries with the same path will collide in the cache. You can prevent this by enabling query key prefixing.

```tsx no-check
// Without prefixes - these would collide!
const authQuery = useQuery(trpcAuth.list.queryOptions()); // auth service
const billingQuery = useQuery(trpcBilling.list.queryOptions()); // billing service
```

Enable the feature flag when creating your context:

```tsx no-check title="utils/trpc.ts"
// [...]

const billing = createTRPCContext<BillingRouter, { keyPrefix: true }>();
export const BillingProvider = billing.TRPCProvider;
export const useBilling = billing.useTRPC;
export const createBillingClient = () =>
  createTRPCClient<BillingRouter>({
    links: [
      /* ... */
    ],
  });

const account = createTRPCContext<AccountRouter, { keyPrefix: true }>();
export const AccountProvider = account.TRPCProvider;
export const useAccount = account.useTRPC;
export const createAccountClient = () =>
  createTRPCClient<AccountRouter>({
    links: [
      /* ... */
    ],
  });
```

```tsx no-check title="App.tsx"
import { useState } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import {
  BillingProvider,
  AccountProvider,
  createBillingClient,
  createAccountClient,
} from './utils/trpc';

// [...]

export function App() {
  const [queryClient] = useState(() => new QueryClient());
  const [billingClient] = useState(() => createBillingClient());
  const [accountClient] = useState(() => createAccountClient());

  return (
    <QueryClientProvider client={queryClient}>
      <BillingProvider
        trpcClient={billingClient}
        queryClient={queryClient}
        keyPrefix="billing"
      >
        <AccountProvider
          trpcClient={accountClient}
          queryClient={queryClient}
          keyPrefix="account"
        >
          <div>{/* ... */}</div>
        </AccountProvider>
      </BillingProvider>
    </QueryClientProvider>
  );
}
```

```tsx no-check title="components/MyComponent.tsx"
import { useQuery } from '@tanstack/react-query';
import { useBilling, useAccount } from '../utils/trpc';

// [...]

export function MyComponent() {
  const billing = useBilling();
  const account = useAccount();

  const billingList = useQuery(billing.list.queryOptions());
  const accountList = useQuery(account.list.queryOptions());

  return (
    <div>
      <div>Billing: {JSON.stringify(billingList.data ?? null)}</div>
      <div>Account: {JSON.stringify(accountList.data ?? null)}</div>
    </div>
  );
}
```

The query keys will be properly prefixed to avoid collisions:

```tsx no-check
// Example of how the query keys look with prefixes
const queryKeys = [
  [['billing'], ['list'], { type: 'query' }],
  [['account'], ['list'], { type: 'query' }],
];
```

### Inferring Input and Output types

When you need to infer the input and output types for a procedure or router, there are 2 options available depending on the situation.

Infer the input and output types of a full router

```ts no-check
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
import type { AppRouter } from './server/router';

export type Inputs = inferRouterInputs<AppRouter>;
export type Outputs = inferRouterOutputs<AppRouter>;
```

Infer types for a single procedure

```ts no-check
import type { inferInput, inferOutput } from '@trpc/tanstack-react-query';

function Component() {
  const trpc = useTRPC();

  type Input = inferInput<typeof trpc.path.to.procedure>;
  type Output = inferOutput<typeof trpc.path.to.procedure>;
}
```

### Accessing the tRPC client

If you used the [setup with React Context](https://trpc.io/docs/client/tanstack-react-query/setup#3a-set-up-the-trpc-context-provider), you can access the tRPC client using the `useTRPCClient` hook.

```tsx no-check
import { useTRPCClient } from './trpc';

async function Component() {
  const trpcClient = useTRPCClient();

  const result = await trpcClient.getUser.query({
    id: '1',
  });
}
```

If you [setup without React Context](https://trpc.io/docs/client/tanstack-react-query/setup#3c-set-up-without-react-context),
you can import the global client instance directly instead.

```ts no-check
import { client } from './trpc';

const result = await client.path.to.procedure.query({
  /** input */
  id: 'foo',
});
```
