This is an extremely crude example for a function that uses your openapi-typescript
output to make a dynamic hook with react-query
.
If you find this useful and make changes, please comment them!
Last active
December 30, 2024 14:45
-
-
Save AshMW2724/7c7d248c35db3a894376686025e2df67 to your computer and use it in GitHub Desktop.
openapi-typescript react-query
This file contains hidden or 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
// generated paths | |
import { paths } from '@/data/@generated/api'; | |
import { UseQueryOptions, UseQueryResult, useQuery } from '@tanstack/react-query'; | |
import { FetchOptions, FetchResponse } from 'openapi-fetch'; | |
// Required! `pnpm i -D openapi-typescript-helpers` | |
import type { FilterKeys, PathsWithMethod, HasRequiredKeys, HttpMethod } from 'openapi-typescript-helpers'; | |
// Import your client generated by `openapi-fetch` | |
import api from './api'; | |
type QueryOptions = { queryOpts?: Partial<UseQueryOptions> }; | |
type CreateUseQuery<Paths extends {}> = { | |
useFetch<T extends HttpMethod, P extends PathsWithMethod<Paths, T>>( | |
method: T, | |
url: P, | |
...init: HasRequiredKeys<FetchOptions<FilterKeys<Paths[P], T>>> extends never | |
? [((FetchOptions<FilterKeys<Paths[P], T>> & QueryOptions) | undefined)?] | |
: [FetchOptions<FilterKeys<Paths[P], T>> & QueryOptions] | |
): UseQueryResult< | |
FetchResponse<T extends keyof Paths[P] ? Paths[P][T] : unknown>['data'], | |
FetchResponse<T extends keyof Paths[P] ? Paths[P][T] : unknown>['error'] | |
>; | |
}; | |
export const createUseQuery: CreateUseQuery<paths> = { | |
// @ts-expect-error It does return the correct type | |
useFetch(method, url, ...init) { | |
const options = init[0]; | |
return useQuery({ | |
// @ts-expect-error Params does exist sometimes | |
queryKey: [url, options?.body, options?.params], | |
queryFn: async ({ signal }) => { | |
// @ts-expect-error All good, we know this method exists | |
const { data, error } = await api.fetch[method.toUpperCase()](url, { | |
// ^^^^^^^^^ This is your client generated by `openapi-fetch` | |
...options, | |
signal, | |
}); | |
if (data) return data; | |
throw new Error(error); | |
}, | |
...options?.queryOpts, | |
}); | |
}, | |
}; |
I'm using this in a tiny project that I set up, and with the following deps, there are a couple typechecking / usage changes that needed adjustment:
"dependencies": {
"@tanstack/react-query": "^5.24.8",
"openapi-fetch": "^0.9.2",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
I had to add the adjusted fetch options to the type signature in the UseQueryResult legs, and replaced api.fetch
with just api
(it's the return value of createClient<paths>()
in my app, which doesn't have a .fetch
key. There was a spurious ts-expect-error
too, that I removed. Here's the result, which works really well so far:
import { paths } from "./v1";
import {
UseQueryOptions,
UseQueryResult,
useQuery,
} from "@tanstack/react-query";
import { FetchOptions, FetchResponse } from "openapi-fetch";
import type {
FilterKeys,
PathsWithMethod,
HasRequiredKeys,
HttpMethod,
} from "openapi-typescript-helpers";
// Import your client generated by `openapi-fetch`
import api from "./openapi-client";
type QueryOptions = { queryOpts?: Partial<UseQueryOptions> };
type CreateUseQuery<Paths extends {}> = {
useFetch<T extends HttpMethod, P extends PathsWithMethod<Paths, T>>(
method: T,
url: P,
...init: HasRequiredKeys<
FetchOptions<FilterKeys<Paths[P], T>>
> extends never
? [((FetchOptions<FilterKeys<Paths[P], T>> & QueryOptions) | undefined)?]
: [FetchOptions<FilterKeys<Paths[P], T>> & QueryOptions]
): UseQueryResult<
FetchResponse<
T extends keyof Paths[P] ? Paths[P][T] : unknown,
FetchOptions<FilterKeys<Paths[P], T>> & QueryOptions
>["data"],
FetchResponse<
T extends keyof Paths[P] ? Paths[P][T] : unknown,
FetchOptions<FilterKeys<Paths[P], T>> & QueryOptions
>["error"]
>;
};
export const createUseQuery: CreateUseQuery<paths> = {
// @ts-expect-error It does return the correct type
useFetch(method, url, ...init) {
const options = init[0];
return useQuery({
queryKey: [url, options?.body, options?.params],
queryFn: async ({ signal }) => {
// @ts-expect-error All good, we know this method exists
const { data, error } = await api[method.toUpperCase()](url, {
...options,
signal,
});
if (data) return data;
throw new Error(error);
},
...options?.queryOpts,
});
},
};
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Looks good, I haven't tested it yet.
But how about useMutaion, any hook for this?
Could you provide a code example of how to use it?