import { InstallTabs } from '@site/src/components/InstallTabs';

# Setup

Fully typed RPC-like client for React Query v5? Oh, yes!

```tsx
const {
  data,                   // <- fully typed response data
  error,                  // <- fully typed error data
} = tsr.getPost.useQuery({
  queryKey: ['posts'],
  queryData: {            // <- fully typed request data
    params: { id: '1' },
  },
  staleTime: 1000,        // <- react-query options (optional)
});
```

:::info

This documentation is for React Query v5. If you are looking for the v4 docs, please click [here](/docs/react-query/v4).

In order to use the React Query v5 version of `@ts-rest/react-query`, make sure you import `@ts-rest/react-query/v5` in your code and not `@ts-rest/react-query`.

:::

## Instructions

### 1. Installation

<InstallTabs packageName="@ts-rest/react-query @tanstack/react-query@5" />

### 2. Setup React Query

If you are not familiar enough with React Query, check out the [official `@tanstack/react-query` documentation](https://tanstack.com/query/v5/docs/framework/react/overview) to learn more about it and how to set it up.

Before proceeding, make sure you set up your `QueryClientProvider` at the root of your application or layout.

### 3. Initialize ts-rest React Query

Import your contract, and pass it with the client options to the `initTsrReactQuery` function. The client options are the same as the ones you would pass to the `initClient` function in `@ts-rest/core`.

```tsx
import { initTsrReactQuery } from '@ts-rest/react-query/v5';
import { getAccessToken } from '@some-auth-lib/sdk';
import { contract } from './contract';

export const tsr = initTsrReactQuery(contract, {
  baseUrl: 'http://localhost:3333',
  baseHeaders: {
    'x-app-source': 'ts-rest',
    'x-access-token': () => getAccessToken(),
  },
});
```

:::info

`initTsrReactQuery` uses our the fetch client under the hood, so you can configure it just as you would the fetch client, including passing a custom API fetcher.
For more information, see the [fetch client documentation](/docs/core/fetch).

:::

### 4. Setup ts-rest Provider

Add the `tsr.ReactQueryProvider` to your root component, just below the `QueryClientProvider`. It is important that it lives as a child of the `QueryClientProvider` so that ts-rest can access the query client.

```tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';
import { tsr } from './tsr';

const queryClient = new QueryClient()

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <QueryClientProvider client={queryClient}>
      <tsr.ReactQueryProvider>{children}</tsr.ReactQueryProvider>
    </QueryClientProvider>
  );
}
```

Now use the hooks from `tsr` in your components. The structure of the `tsr` container follows the same structure as your contract.

## Complete Example

```tsx
import { tsr } from './tsr';

const Posts = () => {
  const tsrQueryClient = tsr.useQueryClient();

  const { data, isPending } = tsr.posts.get.useQuery({ queryKey: ['posts'] });
  const { mutate } = tsr.posts.create.useMutation({
    onMutate: (newPost) => {
      // get current posts, so we can reset back to it if the mutation fails
      const lastGoodKnown = tsrQueryClient.posts.get.getQueryData(['posts']);

      // optimistically update the cache with the new post
      tsrQueryClient.posts.get.setQueryData(['posts'], (old) => ({
        ...old,
        body: [
          ...old.body,
          {
            ...newPost.body,
            id: `placeholder-${Date.now()}`
          }
        ]
      }));

      // return the old posts to be stored in mutation context
      return { lastGoodKnown };
    },
    onError: (error, newPost, context) => {
      tsrQueryClient.posts.get.setQueryData(['posts'], context.lastGoodKnown);
    },
    onSettled: () => {
      // trigger a refetch regardless of it the mutation was successful or not
      tsrQueryClient.invalidateQueries({ queryKey: ['posts'] });
      //                 ^ QueryClient functions that do not consume or provide typed data are not wrapped by ts-rest
      // and are provided at the root level only
    },
  });

  if (isPending) {
    return <div>Loading...</div>;
  }

  if (data?.status !== 200) {
    return <div>Error</div>;
  }

  return (
    <div>
      <ul>
        {data.body.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
      <button onClick={() => mutate({ body: { title: 'Hello World' } })}>Create Post</button>
    </div>
  );
};
```

All the hooks have the same function signatures and options exactly as the ones from `@tanstack/react-query`, with the only difference being that you need to pass `queryData` instead of `queryFn`.

:::info

When destructing the response from `useQuery` or `useMutation`, remember that ts-rest returns a `status` and `body` property, so you'll need to destructure those as well.

:::

## Directly Fetching

If you want to make `fetch` requests directly without going through React Query, we also expose `query` and `mutate` functions, so you do not have to initialize a separate fetch client.

```typescript
// Normal fetch
const { body, status } = await tsr.posts.get.query();

// useQuery hook
const { data, isLoading } = tsr.posts.get.useQuery();
```
