diff --git a/docs/swr-openapi/use-mutation.md b/docs/swr-openapi/use-mutation.md new file mode 100644 index 000000000..495fee571 --- /dev/null +++ b/docs/swr-openapi/use-mutation.md @@ -0,0 +1,86 @@ +--- +title: useMutation +--- + +# {{ $frontmatter.title }} + +`useMutation` is a wrapper around SWR's [useSWRMutation][swr-use-mutation] function. It provides a type-safe hook for remote mutations. + +```tsx +import createClient from "openapi-fetch"; +import type { paths } from "./my-openapi-3-schema"; // generated types + +const client = createClient({ baseUrl: "https://my-api.com" }); +const useMutation = createMutationHook(client, "my-api"); + +function MyComponent() { + const { trigger, data, isMutating } = useMutation("/users/{userId}", "post", { + params: { + userId: "123", + }, + }); + + return ( + + ); +} +``` + +## API + +### Parameters + +- `key`: + - `path`: Any endpoint that supports `GET` requests. + - `init`: (_optional_) Partial fetch options for the chosen endpoint. +- `method`: HTTP method for the chosen endpoint. +- `options`: (_optional_) [SWR mutate options][swr-use-mutation-params]. + +### Returns + +- Return of a [useSWRMutation][swr-mutation-response] including: + +`data`: data for the given key returned from fetcher +`error`: error thrown by fetcher (or undefined) +`trigger(arg, options)`: a function to trigger a remote mutation +`reset`: a function to reset the state (data, error, isMutating) +`isMutating`: if there's an ongoing remote mutation + +## How It Works + +```ts +function useMutation( + path, + method, + config, +) { + const key = [prefix, path, method]; + + return useSWRMutation( + key, + async (_key, { arg }) => { + const m = method.toUpperCase(); + + const res = await client[m](path, arg); + if (res.error) { + throw res.error; + } + return res.data; + }, + config, + ); +}; + +``` + +[swr-mutate-params]: https://swr.vercel.app/docs/mutation#parameters +[swr-use-mutation]: https://swr.vercel.app/docs/mutation#useswrmutation +[swr-use-mutation-params]: https://swr.vercel.app/docs/mutation#useswrmutation-parameters +[swr-mutation-response]: https://swr.vercel.app/docs/mutation#useswrmutation-return-values diff --git a/packages/swr-openapi/CHANGELOG.md b/packages/swr-openapi/CHANGELOG.md index fd4467996..96d9e8d1c 100644 --- a/packages/swr-openapi/CHANGELOG.md +++ b/packages/swr-openapi/CHANGELOG.md @@ -1,5 +1,11 @@ # swr-openapi +## 5.5.0 + +### Minor Changes + +- [#2552](https://github.com/openapi-ts/openapi-typescript/pull/2552) [`072543f`](https://github.com/openapi-ts/openapi-typescript/pull/2552/commits/072543fc1723b81eae2d99639d7c36bd8db94126) Thanks [@prescottprue](https://github.com/prescottprue)! - Add useMutation hook + ## 5.4.2 ### Patch Changes diff --git a/packages/swr-openapi/package.json b/packages/swr-openapi/package.json index 9813d2f02..36c7bb768 100644 --- a/packages/swr-openapi/package.json +++ b/packages/swr-openapi/package.json @@ -1,7 +1,7 @@ { "name": "swr-openapi", "description": "Generate SWR hooks from OpenAPI schemas", - "version": "5.4.2", + "version": "5.5.0", "author": { "name": "Hunter Tunnicliff", "email": "hunter@tunnicliff.co" diff --git a/packages/swr-openapi/src/index.ts b/packages/swr-openapi/src/index.ts index 5f9b3300d..942730171 100644 --- a/packages/swr-openapi/src/index.ts +++ b/packages/swr-openapi/src/index.ts @@ -1,5 +1,6 @@ export * from "./immutable.js"; export * from "./infinite.js"; export * from "./mutate.js"; +export * from "./mutation.js"; export * from "./query.js"; export * from "./types.js"; diff --git a/packages/swr-openapi/src/mutation.ts b/packages/swr-openapi/src/mutation.ts new file mode 100644 index 000000000..11c45148e --- /dev/null +++ b/packages/swr-openapi/src/mutation.ts @@ -0,0 +1,66 @@ +import type { Client } from "openapi-fetch"; +import type { HttpMethod, MediaType, PathsWithMethod } from "openapi-typescript-helpers"; +import useSWRMutation, { type SWRMutationConfiguration, type SWRMutationResponse } from "swr/mutation"; +import type { TypesForRequest } from "./types.js"; +import { useMemo } from "react"; + +/** + * Produces a typed wrapper for [`useSWRMutation`](https://swr.vercel.app/docs/mutation). + * + * ```ts + * import createClient from "openapi-fetch"; + * import type { paths } from "./my-openapi-3-schema"; // generated types + * + * const client = createClient({ baseUrl: "https://my-api.com" }); + * const useMutation = createMutationHook(client, "my-api"); + * + * function MyComponent() { + * const { trigger, data, isMutating } = useMutation("/users", "post"); + * + * return ( + * + * ); + * } + * ``` + */ +export function createMutationHook( + client: Client, + prefix: string, +) { + return function useMutation< + Method extends Extract, + Path extends PathsWithMethod, + T extends TypesForRequest = TypesForRequest, + Data = T["Data"], + Error = T["Error"], + Init = T["Init"], + >( + path: Path, + method: Method, + init: Init | null, + config?: SWRMutationConfiguration, + ): SWRMutationResponse { + const key = useMemo(() => (init !== null ? ([prefix, path, init] as const) : null), [prefix, path, init]); + + return useSWRMutation( + key, + async (_key, { arg }) => { + const m = method.toUpperCase() as Uppercase; + + const res = await (client as any)[m](path, arg); + if (res.error) { + throw res.error; + } + return res.data; + }, + config, + ); + }; +}