# @openapi-qraft/react

`@openapi-qraft/react` is a modular TypeScript client designed to facilitate type-safe API requests in React
applications,
leveraging the power of **TanStack Query v5**. It utilizes a Proxy-based architecture to dynamically generate
hooks with typed parameters, ensuring that your API requests are both type-safe and efficient.

Read the full documentation at [openapi-qraft.github.io/openapi-qraft](https://openapi-qraft.github.io/openapi-qraft/).

## Features

- **Type-safe API Requests:** Utilize TypeScript for type-safe API requests, reducing runtime errors and improving
  developer experience.
- **Modular Design:** Customize the utility with a set of callbacks to handle API calls according to your project's
  needs.
- **Integration with [TanStack Query v5](https://tanstack.com/query/v5):** Seamlessly integrate with _TanStack Query_
  for handling server state, caching, and data synchronization.
- **Dynamic Proxy-Based Hooks:** Automatically generate React Query hooks for your API endpoints without manual
  boilerplate.

## Installation

First, install the core package for your project:

```bash
npm install @openapi-qraft/react
```

If your project doesn't already include `@tanstack/react-query`, you'll also need to install it. This package is
essential for handling server state in React applications:

```bash
npm install @tanstack/react-query
```

## Getting Started

To get started with OpenAPI Qraft, you need to generate types and service definitions from your OpenAPI Document.
These are used to create type-safe hooks and interact with your API.

### 1. Generate OpenAPI Types & Services

Run the following command in the root directory of your project using your package manager:

```bash
npx @openapi-qraft/cli --plugin tanstack-query-react --plugin openapi-typescript \
  --output-dir src/api https://raw.githubusercontent.com/swagger-api/swagger-petstore/master/src/main/resources/openapi.yaml
```

> 💡 For stable project maintenance, it's recommended to install the CLI generator as a dev dependency.
> Read more about installation in the [documentation](https://openapi-qraft.github.io/openapi-qraft/docs/getting-started/installation).

### 2. Use the generated services in your React application

Below are examples demonstrating how to use the generated services in your React application with
`useQuery`, `useMutation`, and `useInfiniteQuery` hooks from [TanStack Query](https://tanstack.com/query/latest).

#### Example with `useQuery()`

```tsx title="src/App.tsx"
import { requestFn } from '@openapi-qraft/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createAPIClient } from './api'; // generated by OpenAPI Qraft

const queryClient = new QueryClient();

// Use `createAPIClient(...)` to initialize the API client as needed.
// It's a lightweight 🪶 shortcut for working with TanStack Query 🌴
const api = createAPIClient({
  requestFn,
  queryClient,
  baseUrl: 'https://petstore3.swagger.io/api/v3',
});

function ExamplePetDetails({ petId }: { petId: number }) {
  /**
   * Executes the request to the API on mount:
   * ###
   * GET /pet/123456
   **/
  const {
    data: pet,
    isPending,
    error,
  } = api.pet.getPetById.useQuery({
    path: { petId },
  });

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

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

  return <div>Pet Name: {pet?.name}</div>;
}

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <ExamplePetDetails petId={123456} />
    </QueryClientProvider>
  );
}
```

#### Example with `useMutation()`

```tsx title="src/App.tsx"
import { requestFn } from '@openapi-qraft/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createAPIClient } from './api'; // generated by OpenAPI Qraft

const queryClient = new QueryClient();

const api = createAPIClient({
  requestFn,
  queryClient,
  baseUrl: 'https://petstore3.swagger.io/api/v3',
});

function EntityForm({ entityId }: { entityId: string }) {
  const mutation = api.entities.postEntitiesIdDocuments.useMutation({
    // ☝️ useMutation() can be used with `undefined` parameters
    path: {
      entity_id: entityId,
    },
    header: {
      'x-monite-version': '2023-09-01',
    },
  });

  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        const formData = new FormData(event.currentTarget);
        /**
         * Executes the request`:
         * ###
         * POST /entities/3e3e-3e3e-3e3e/documents
         * x-monite-version: 2023-09-01
         *
         * {"company_tax_id_verification": ["verification-id"]}
         **/
        mutation.mutate({
          company_tax_id_verification: [
            String(formData.get('company_tax_id_verification')),
          ],
        });
      }}
    >
      <input name="company_tax_id_verification" />
      <button>Submit</button>
    </form>
  );
}

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <EntityForm entityId="3e3e-3e3e-3e3e" />
    </QueryClientProvider>
  );
}
```

#### Example with `useInfiniteQuery()`

```tsx title="src/PostList.tsx"
import { requestFn } from '@openapi-qraft/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createAPIClient } from './api'; // generated by OpenAPI Qraft

const queryClient = new QueryClient();

const api = createAPIClient({
  requestFn,
  queryClient,
  baseUrl: 'https://petstore3.swagger.io/api/v3',
});

/**
 * Executes the initial request:
 * ###
 * GET /posts?limit=10&page=1
 **/
function PostList() {
  const infiniteQuery = api.posts.getPosts.useInfiniteQuery(
    { query: { limit: 10 } },
    {
      getNextPageParam: (lastPage, allPages, lastPageParams) => {
        if (lastPage.length < 10) return; // if less than 10 items, there are no more pages
        return {
          query: {
            page: Number(lastPageParams.query?.page) + 1,
          },
        };
      },
      initialPageParam: {
        query: {
          page: 1, // will be used in initial request
        },
      },
    }
  );

  return (
    <div>
      {infiniteQuery.data?.pages.map((page, pageIndex) => (
        <ul key={pageIndex}>
          {page.map((post) => (
            <li key={post.id}>{post.title}</li>
          ))}
        </ul>
      ))}
      <button
        onClick={() => {
          // ⬇︎ Executes GET /posts?limit=10&page=2
          infiniteQuery.fetchNextPage();
        }}
      >
        Load More
      </button>
    </div>
  );
}

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <PostList />
    </QueryClientProvider>
  );
}
```

## Supported Hooks

- [x] [`useQuery(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/hooks/useQuery)
- [x] [`useMutation(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/hooks/useMutation)
- [x] [`useInfiniteQuery(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/hooks/useInfiniteQuery)
- [x] [`useQueries(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/hooks/useQueries)
- [x] [`useSuspenseQuery(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/hooks/useSuspenseQuery)
- [x] [`useSuspenseInfiniteQuery(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/hooks/useSuspenseInfiniteQuery)
- [x] [`useSuspenseQueries(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/hooks/useSuspenseQueries)
- [x] [`useIsFetching(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/hooks/useIsFetching)
- [x] [`useMutationState(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/hooks/useMutationState)
- [x] [`useIsMutating(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/hooks/useIsMutating)

## `QueryClient` methods

- [x] [`fetchQuery(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/fetchQuery)
- [x] [`fetchInfiniteQuery(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/fetchInfiniteQuery)
- [x] [`prefetchQuery(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/prefetchQuery)
- [x] [`prefetchInfiniteQuery(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/prefetchInfiniteQuery)
- [x] [`getQueryData(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/getQueryData)
- [x] [`getQueriesData(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/getQueriesData)
- [x] [`setQueryData(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/setQueryData)
- [x] [`getQueryState(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/getQueryState)
- [x] [`setQueriesData(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/setQueriesData)
- [x] [`invalidateQueries(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/invalidateQueries)
- [x] [`refetchQueries(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/refetchQueries)
- [x] [`cancelQueries(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/cancelQueries)
- [x] [`removeQueries(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/removeQueries)
- [x] [`resetQueries(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/resetQueries)
- [x] [`isFetching(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/isFetching)
- [x] [`isMutating(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/isMutating)

## Qraft Utility Functions

- [x] [`getQueryKey(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/getQueryKey)
- [x] [`setInfiniteQueryData(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/setInfiniteQueryData)
- [x] [`getInfiniteQueryKey(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/getInfiniteQueryKey)
- [x] [`getInfiniteQueryData(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/getInfiniteQueryData)
- [x] [`getInfiniteQueryState(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/getInfiniteQueryState)
- [x] [`getMutationKey(...)`](https://openapi-qraft.github.io/openapi-qraft/docs/query-client/getMutationKey)

## Contributing

Contributions are welcome! If you have suggestions or want to improve `@openapi-qraft/react`, please feel free to submit
a pull request or open an issue.

## License

`@openapi-qraft/react` is licensed under the MIT License. See the LICENSE file for more details.
