Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

openapi-react-query #1717

Merged
merged 21 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/.vitepress/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ export const en = defineConfig({
{ text: "About", link: "/openapi-fetch/about" },
],
},
{
text: "openapi-react-query",
items: [
{ text: "Getting Started", link: "/openapi-react-query/" },
{ text: "useQuery", link: "/openapi-react-query/use-query" },
{ text: "useMutation", link: "/openapi-react-query/use-mutation" },
{ text: "useSuspenseQuery", link: "/openapi-react-query/use-suspense-query" },
{ text: "About", link: "/openapi-react-query/about" },
],
},
],
},
search: {
Expand Down
2 changes: 1 addition & 1 deletion docs/data/contributors.json

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions docs/openapi-react-query/about.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
title: About openapi-react-query
description: openapi-react-query Project Goals and contributors
---
<script setup>
import { VPTeamMembers } from 'vitepress/theme';
import contributors from '../data/contributors.json';
</script>

# About

## Project Goals

1. Types should be strict and inferred automatically from OpenAPI schemas with the absolute minimum number of generics needed.
2. Respect the original `@tanstack/react-query` APIs while reducing boilerplate.
3. Be as light and performant as possible.

## Contributors

This library wouldn’t be possible without all these amazing contributors:

<VPTeamMembers size="small" :members="contributors['openapi-react-query']" />

110 changes: 110 additions & 0 deletions docs/openapi-react-query/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
title: openapi-react-query
---
# Introduction

openapi-react-query is a type-safe tiny wrapper (1 kb) around [@tanstack/react-query](https://tanstack.com/query/latest/docs/framework/react/overview) to work with OpenAPI schema.

It works by using [openapi-fetch](../openapi-fetch/) and [openapi-typescript](../introduction) so you get all the following features:

- ✅ No typos in URLs or params.
- ✅ All parameters, request bodies, and responses are type-checked and 100% match your schema
- ✅ No manual typing of your API
- ✅ Eliminates `any` types that hide bugs
- ✅ Also eliminates `as` type overrides that can also hide bugs

::: code-group

```tsx [src/my-component.ts]
import createFetchClient from "openapi-fetch";
import createClient from "openapi-react-query";
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript

const fetchClient = createFetchClient<paths>({
baseUrl: "https://myapi.dev/v1/",
});
const $api = createClient(fetchClient);

const MyComponent = () => {
const { data, error, isLoading } = $api.useQuery(
"get",
"/blogposts/{post_id}",
{
params: {
path: { post_id: 5 },
},
},
);

if (isLoading || !data) return "Loading...";

if (error) return `An error occured: ${error.message}`;

return <div>{data.title}</div>;
};
```

:::

## Setup

Install this library along with [openapi-fetch](../openapi-fetch/) and [openapi-typescript](../introduction):

```bash
npm i openapi-react-query openapi-fetch
npm i -D openapi-typescript typescript
```

::: tip Highly recommended

Enable [noUncheckedIndexedAccess](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess) in your `tsconfig.json` ([docs](/advanced#enable-nouncheckedindexaccess-in-your-tsconfigjson))

:::

Next, generate TypeScript types from your OpenAPI schema using openapi-typescript:

```bash
npx openapi-typescript ./path/to/api/v1.yaml -o ./src/lib/api/v1.d.ts
```

## Basic usage

Once your types has been generated from your schema, you can create a [fetch client](../introduction.md), a react-query client and start querying your API.

::: code-group

```tsx [src/my-component.ts]
kerwanp marked this conversation as resolved.
Show resolved Hide resolved
import createFetchClient from "openapi-fetch";
import createClient from "openapi-react-query";
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript

const fetchClient = createFetchClient<paths>({
baseUrl: "https://myapi.dev/v1/",
});
const $api = createClient(fetchClient);

const MyComponent = () => {
const { data, error, isLoading } = $api.useQuery(
"get",
"/blogposts/{post_id}",
{
params: {
path: { post_id: 5 },
},
},
);

if (isLoading || !data) return "Loading...";

if (error) return `An error occured: ${error.message}`;

return <div>{data.title}</div>;
};
```

:::

::: tip
You can find more information about `createFetchClient` on the [openapi-fetch documentation](../openapi-fetch/index.md).
:::

65 changes: 65 additions & 0 deletions docs/openapi-react-query/use-mutation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
title: useQuery
kerwanp marked this conversation as resolved.
Show resolved Hide resolved
---
# {{ $frontmatter.title }}

The `useMutation` method allows you to use the original [useMutation](https://tanstack.com/query/latest/docs/framework/react/guides/queries).
kerwanp marked this conversation as resolved.
Show resolved Hide resolved

- The result is the same as the original function.
- The `mutationKey` is `[method, path]`.
- `data` and `error` are fully typed.

::: tip
You can find more information about `useMutation` on the [@tanstack/react-query documentation](https://tanstack.com/query/latest/docs/framework/react/guides/mutations).
:::

## Example

::: code-group

```tsx [src/app.tsx]
import { $api } from "./api";

export const App = () => {
const { mutate } = $api.useMutation("patch", "/users");

return (
<button onClick={() => mutate({ body: { firstname: "John" } })}>
Update
</button>
);
};
```

```ts [src/api.ts]
import createFetchClient from "openapi-fetch";
import createClient from "openapi-react-query";
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript

const fetchClient = createFetchClient<paths>({
baseUrl: "https://myapi.dev/v1/",
});
export const $api = createClient(fetchClient);
```

:::

## Api

```tsx
const query = $api.useQuery(method, path, options, queryOptions);
```

**Arguments**

- `method` **(required)**
- The HTTP method to use for the request.
- The method is used as key. See [Query Keys](https://tanstack.com/query/latest/docs/framework/react/guides/query-keys) for more information.
- `path` **(required)**
- The pathname to use for the request.
- Must be an available path for the given method in your schema.
- The pathname is used as key. See [Query Keys](https://tanstack.com/query/latest/docs/framework/react/guides/query-keys) for more information.
- `queryOptions`
- The original `useMutation` options.
- [See more information](https://tanstack.com/query/latest/docs/framework/react/reference/useMutation)

73 changes: 73 additions & 0 deletions docs/openapi-react-query/use-query.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
title: useQuery
---
# {{ $frontmatter.title }}

The `useQuery` method allows you to use the original [useQuery](https://tanstack.com/query/latest/docs/framework/react/guides/queries).

- The result is the same as the original function.
- The `functionKey` is `[method, path, params]`.
- `data` and `error` are fully typed.
- You can pass queries options as fourth parameter.

::: tip
You can find more information about `useQuery` on the [@tanstack/react-query documentation](https://tanstack.com/query/latest/docs/framework/react/guides/queries).
:::

## Example

::: code-group

```tsx [src/app.tsx]
import { $api } from "./api";

export const App = () => {
const { data, error, isLoading } = $api.useQuery("get", "/users/{user_id}", {
params: {
path: { user_id: 5 },
},
});

if (!data || isLoading) return "Loading...";
if (error) return `An error occured: ${error.message}`;

return <div>{data.firstname}</div>;
};
```

```ts [src/api.ts]
import createFetchClient from "openapi-fetch";
import createClient from "openapi-react-query";
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript

const fetchClient = createFetchClient<paths>({
baseUrl: "https://myapi.dev/v1/",
});
export const $api = createClient(fetchClient);
```

:::

## Api

```tsx
const query = $api.useQuery(method, path, options, queryOptions);
```

**Arguments**

- `method` **(required)**
- The HTTP method to use for the request.
- The method is used as key. See [Query Keys](https://tanstack.com/query/latest/docs/framework/react/guides/query-keys) for more information.
- `path` **(required)**
- The pathname to use for the request.
- Must be an available path for the given method in your schema.
- The pathname is used as key. See [Query Keys](https://tanstack.com/query/latest/docs/framework/react/guides/query-keys) for more information.
- `options`
- The fetch options to use for the request.
- Only required if the OpenApi schema requires parameters.
- The options `params` are used as key. See [Query Keys](https://tanstack.com/query/latest/docs/framework/react/guides/query-keys) for more information.
- `queryOptions`
- The original `useQuery` options.
- [See more information](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery)

79 changes: 79 additions & 0 deletions docs/openapi-react-query/use-suspense-query.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
title: useSuspenseQuery
---
# {{ $frontmatter.title }}

The `useSuspenseQuery` method allows you to use the original [useSuspenseQuery](https://tanstack.com/query/latest/docs/framework/react/guides/suspense).

- The result is the same as the original function.
- The `functionKey` is `[method, path, params]`.
- `data` and `error` are fully typed.
- You can pass queries options as fourth parameter.

::: tip
You can find more information about `useSuspenseQuery` on the [@tanstack/react-query documentation](https://tanstack.com/query/latest/docs/framework/react/guides/suspense).
:::

## Example

::: code-group

```tsx [src/app.tsx]
import { ErrorBoundary } from "react-error-boundary";
import { $api } from "./api";

const MyComponent = () => {
const { data } = $api.useSuspenseQuery("get", "/users/{user_id}", {
params: {
path: { user_id: 5 },
},
});

return <div>{data.firstname}</div>;
};

export const App = () => {
return (
<ErrorBoundary fallbackRender={({ error }) => `Error: ${error.message}`}>
<MyComponent />
</ErrorBoundary>
);
};
```

```ts [src/api.ts]
import createFetchClient from "openapi-fetch";
import createClient from "openapi-react-query";
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript

const fetchClient = createFetchClient<paths>({
baseUrl: "https://myapi.dev/v1/",
});
export const $api = createClient(fetchClient);
```

:::

## Api

```tsx
const query = $api.useSuspenseQuery(method, path, options, queryOptions);
```

**Arguments**

- `method` **(required)**
- The HTTP method to use for the request.
- The method is used as key. See [Query Keys](https://tanstack.com/query/latest/docs/framework/react/guides/query-keys) for more information.
- `path` **(required)**
- The pathname to use for the request.
- Must be an available path for the given method in your schema.
- The pathname is used as key. See [Query Keys](https://tanstack.com/query/latest/docs/framework/react/guides/query-keys) for more information.
- `options`
- The fetch options to use for the request.
- Only required if the OpenApi schema requires parameters.
- The options `params` are used as key. See [Query Keys](https://tanstack.com/query/latest/docs/framework/react/guides/query-keys) for more information.
- `queryOptions`
- The original `useSuspenseQuery` options.
- [See more information](https://tanstack.com/query/latest/docs/framework/react/reference/useSuspenseQuery)

13 changes: 10 additions & 3 deletions docs/scripts/update-contributors.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,12 +182,19 @@ export const OPENAPI_FETCH_CONTRIBUTORS = [
]),
];

export const OPENAPI_REACT_QUERY_CONTRIBUTORS = [...new Set(["drwpow", "kerwanp"])];

async function main() {
const total = [...OPENAPI_TS_CONTRIBUTORS, OPENAPI_FETCH_CONTRIBUTORS].length;
const total = [...OPENAPI_TS_CONTRIBUTORS, OPENAPI_FETCH_CONTRIBUTORS, OPENAPI_REACT_QUERY_CONTRIBUTORS].length;
let i = 0;
await Promise.all(
["openapi-typescript", "openapi-fetch"].map(async (repo) => {
const userlist = repo === "openapi-fetch" ? OPENAPI_FETCH_CONTRIBUTORS : OPENAPI_TS_CONTRIBUTORS;
["openapi-typescript", "openapi-fetch", "openapi-react-query"].map(async (repo) => {
kerwanp marked this conversation as resolved.
Show resolved Hide resolved
const userlist =
repo === "openapi-fetch"
? OPENAPI_FETCH_CONTRIBUTORS
: repo === "openapi-react-query"
? OPENAPI_REACT_QUERY_CONTRIBUTORS
: OPENAPI_TS_CONTRIBUTORS;
for (const username of userlist) {
// skip profiles that have been updated within the past week
const { lastFetch } = contributors[repo].find((u) => u.username === username) ?? { lastFetch: 0 };
Expand Down
Loading
Loading