-
-
Notifications
You must be signed in to change notification settings - Fork 481
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add unmodified source * Update package.json * Add minor changeset * Fix funding * Fix path for windows * Replace ESLint and Prettier with Biome * Add documentation * Enable deep on page outlines * Update lockfile * Replace README docs with link to Vitepress docs * Update contributors.json * Fix del cli
- Loading branch information
1 parent
ff57082
commit 639ec45
Showing
35 changed files
with
3,911 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"swr-openapi": minor | ||
--- | ||
|
||
Modify package.json to point to new repository |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
--- | ||
title: About swr-openapi | ||
description: swr-openapi Project Goals and contributors | ||
--- | ||
<script setup> | ||
import { VPTeamMembers } from 'vitepress/theme'; | ||
import contributors from '../data/contributors.json'; | ||
</script> | ||
|
||
# {{ $frontmatter.title }} | ||
|
||
## 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 `swr` 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['swr-openapi']" /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
--- | ||
title: Hook Builders | ||
--- | ||
|
||
# {{ $frontmatter.title }} | ||
|
||
Hook builders initialize `useQuery`, `useImmutate`, `useInfinite`, and `useMutate`. | ||
|
||
Each builder function accepts an instance of a [fetch client](../openapi-fetch/api.md) and a prefix unique to that client. | ||
|
||
|
||
::: tip | ||
|
||
Prefixes ensure that `swr` will avoid caching requests from different APIs when requests happen to match (e.g. `GET /health` for "API A" and `GET /health` for "API B"). | ||
|
||
::: | ||
|
||
```ts | ||
import createClient from "openapi-fetch"; | ||
import { isMatch } from "lodash-es"; | ||
|
||
import { | ||
createQueryHook, | ||
createImmutableHook, | ||
createInfiniteHook, | ||
createMutateHook, | ||
} from "swr-openapi"; | ||
|
||
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript | ||
|
||
const client = createClient<paths>(/* ... */); | ||
const prefix = "my-api"; | ||
|
||
export const useQuery = createQueryHook(client, prefix); | ||
export const useImmutable = createImmutableHook(client, prefix); | ||
export const useInfinite = createInfiniteHook(client, prefix); | ||
export const useMutate = createMutateHook( | ||
client, | ||
prefix, | ||
isMatch, // Or any comparision function | ||
); | ||
``` | ||
|
||
## API | ||
|
||
### Parameters | ||
|
||
Each builder hook accepts the same initial parameters: | ||
|
||
- `client`: A [fetch client](../openapi-fetch/api.md). | ||
- `prefix`: A prefix unique to the fetch client. | ||
|
||
`createMutateHook` also accepts a third parameter: | ||
|
||
- [`compare`](#compare): A function to compare fetch options). | ||
|
||
### Returns | ||
|
||
- `createQueryHook` → [`useQuery`](./use-query.md) | ||
- `createImmutableHook` → [`useImmutable`](./use-immutable.md) | ||
- `createInfiniteHook` → [`useInfinite`](./use-infinite.md) | ||
- `createMutateHook` → [`useMutate`](./use-mutate.md) | ||
|
||
## `compare` | ||
|
||
When calling `createMutateHook`, a function must be provided with the following contract: | ||
|
||
```ts | ||
type Compare = (init: any, partialInit: object) => boolean; | ||
``` | ||
|
||
This function is used to determine whether or not a cached request should be updated when `mutate` is called with fetch options. | ||
|
||
My personal recommendation is to use lodash's [`isMatch`][lodash-is-match]: | ||
|
||
> Performs a partial deep comparison between object and source to determine if object contains equivalent property values. | ||
```ts | ||
const useMutate = createMutateHook(client, "<unique-key>", isMatch); | ||
|
||
const mutate = useMutate(); | ||
|
||
await mutate([ | ||
"/path", | ||
{ | ||
params: { | ||
query: { | ||
version: "beta", | ||
}, | ||
}, | ||
}, | ||
]); | ||
|
||
// ✅ Would be updated | ||
useQuery("/path", { | ||
params: { | ||
query: { | ||
version: "beta", | ||
}, | ||
}, | ||
}); | ||
|
||
// ✅ Would be updated | ||
useQuery("/path", { | ||
params: { | ||
query: { | ||
version: "beta", | ||
other: true, | ||
example: [1, 2, 3], | ||
}, | ||
}, | ||
}); | ||
|
||
// ❌ Would not be updated | ||
useQuery("/path", { | ||
params: { | ||
query: {}, | ||
}, | ||
}); | ||
|
||
// ❌ Would not be updated | ||
useQuery("/path"); | ||
|
||
// ❌ Would not be updated | ||
useQuery("/path", { | ||
params: { | ||
query: { | ||
version: "alpha", | ||
}, | ||
}, | ||
}); | ||
|
||
// ❌ Would not be updated | ||
useQuery("/path", { | ||
params: { | ||
query: { | ||
different: "items", | ||
}, | ||
}, | ||
}); | ||
``` | ||
|
||
[lodash-is-match]: https://lodash.com/docs/4.17.15#isMatch |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
--- | ||
title: swr-openapi | ||
--- | ||
|
||
# Introduction | ||
|
||
swr-openapi is a type-safe wrapper around [`swr`](https://swr.vercel.app). | ||
|
||
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 createClient from "openapi-fetch"; | ||
import { createQueryHook } from "swr-openapi"; | ||
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript | ||
|
||
const client = createClient<paths>({ | ||
baseUrl: "https://myapi.dev/v1/", | ||
}); | ||
|
||
const useQuery = createQueryHook(client, "my-api"); | ||
|
||
function MyComponent() { | ||
const { data, error, isLoading, isValidating, mutate } = useQuery( | ||
"/blogposts/{post_id}", | ||
{ | ||
params: { | ||
path: { post_id: "123" }, | ||
}, | ||
}, | ||
); | ||
|
||
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 swr-openapi 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-nouncheckedindexedaccess-in-tsconfig)) | ||
|
||
::: | ||
|
||
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 types have been generated from your schema, you can create a [fetch client](../introduction.md) and export wrapped `swr` hooks. | ||
|
||
|
||
Wrapper hooks are provided 1:1 for each hook exported by SWR. Check out the other sections of this documentation to learn more about each one. | ||
|
||
::: code-group | ||
|
||
```ts [src/my-api.ts] | ||
import createClient from "openapi-fetch"; | ||
import { | ||
createQueryHook, | ||
createImmutableHook, | ||
createInfiniteHook, | ||
createMutateHook, | ||
} from "swr-openapi"; | ||
import { isMatch } from "lodash-es"; | ||
import type { paths } from "./my-openapi-3-schema"; // generated by openapi-typescript | ||
|
||
const client = createClient<paths>({ | ||
baseUrl: "https://myapi.dev/v1/", | ||
}); | ||
const prefix = "my-api"; | ||
|
||
export const useQuery = createQueryHook(client, prefix); | ||
export const useImmutable = createImmutableHook(client, prefix); | ||
export const useInfinite = createInfiniteHook(client, prefix); | ||
export const useMutate = createMutateHook( | ||
client, | ||
prefix, | ||
isMatch, // Or any comparision function | ||
); | ||
``` | ||
::: | ||
|
||
::: tip | ||
You can find more information about `createClient` on the [openapi-fetch documentation](../openapi-fetch/index.md). | ||
::: | ||
|
||
|
||
Then, import these hooks in your components: | ||
|
||
::: code-group | ||
```tsx [src/my-component.tsx] | ||
import { useQuery } from "./my-api"; | ||
|
||
function MyComponent() { | ||
const { data, error, isLoading, isValidating, mutate } = useQuery( | ||
"/blogposts/{post_id}", | ||
{ | ||
params: { | ||
path: { post_id: "123" }, | ||
}, | ||
}, | ||
); | ||
|
||
if (isLoading || !data) return "Loading..."; | ||
|
||
if (error) return `An error occured: ${error.message}`; | ||
|
||
return <div>{data.title}</div>; | ||
} | ||
``` | ||
::: |
Oops, something went wrong.