Skip to content

Commit 1f048dc

Browse files
committed
feat(openapi-react-query): add prefixQueryKey option
1 parent 99c346e commit 1f048dc

File tree

3 files changed

+76
-24
lines changed

3 files changed

+76
-24
lines changed

.changeset/good-buttons-bake.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openapi-react-query': minor
3+
---
4+
5+
Add `prefixQueryKey` to `createClient` to avoid query key collision between different openapi-fetch clients.

packages/openapi-react-query/src/index.ts

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,18 @@ type InferSelectReturnType<TData, TSelect> = TSelect extends (data: TData) => in
3131
type InitWithUnknowns<Init> = Init & { [key: string]: unknown };
3232

3333
export type QueryKey<
34+
Prefix,
3435
Paths extends Record<string, Record<HttpMethod, {}>>,
3536
Method extends HttpMethod,
3637
Path extends PathsWithMethod<Paths, Method>,
3738
Init = MaybeOptionalInit<Paths[Path], Method>,
38-
> = Init extends undefined ? readonly [Method, Path] : readonly [Method, Path, Init];
39+
> = readonly [Prefix, Method, Path, ...(Init extends undefined ? [] : [Init])];
3940

40-
export type QueryOptionsFunction<Paths extends Record<string, Record<HttpMethod, {}>>, Media extends MediaType> = <
41+
export type QueryOptionsFunction<
42+
Paths extends Record<string, Record<HttpMethod, {}>>,
43+
Media extends MediaType,
44+
Prefix = unknown,
45+
> = <
4146
Method extends HttpMethod,
4247
Path extends PathsWithMethod<Paths, Method>,
4348
Init extends MaybeOptionalInit<Paths[Path], Method>,
@@ -47,7 +52,7 @@ export type QueryOptionsFunction<Paths extends Record<string, Record<HttpMethod,
4752
Response["data"],
4853
Response["error"],
4954
InferSelectReturnType<Response["data"], Options["select"]>,
50-
QueryKey<Paths, Method, Path>
55+
QueryKey<Prefix, Paths, Method, Path>
5156
>,
5257
"queryKey" | "queryFn"
5358
>,
@@ -63,7 +68,7 @@ export type QueryOptionsFunction<Paths extends Record<string, Record<HttpMethod,
6368
Response["data"],
6469
Response["error"],
6570
InferSelectReturnType<Response["data"], Options["select"]>,
66-
QueryKey<Paths, Method, Path>
71+
QueryKey<Prefix, Paths, Method, Path>
6772
>,
6873
"queryFn"
6974
> & {
@@ -72,14 +77,18 @@ export type QueryOptionsFunction<Paths extends Record<string, Record<HttpMethod,
7277
Response["data"],
7378
Response["error"],
7479
InferSelectReturnType<Response["data"], Options["select"]>,
75-
QueryKey<Paths, Method, Path>
80+
QueryKey<Prefix, Paths, Method, Path>
7681
>["queryFn"],
7782
SkipToken | undefined
7883
>;
7984
}
8085
>;
8186

82-
export type UseQueryMethod<Paths extends Record<string, Record<HttpMethod, {}>>, Media extends MediaType> = <
87+
export type UseQueryMethod<
88+
Paths extends Record<string, Record<HttpMethod, {}>>,
89+
Media extends MediaType,
90+
Prefix = unknown,
91+
> = <
8392
Method extends HttpMethod,
8493
Path extends PathsWithMethod<Paths, Method>,
8594
Init extends MaybeOptionalInit<Paths[Path], Method>,
@@ -89,7 +98,7 @@ export type UseQueryMethod<Paths extends Record<string, Record<HttpMethod, {}>>,
8998
Response["data"],
9099
Response["error"],
91100
InferSelectReturnType<Response["data"], Options["select"]>,
92-
QueryKey<Paths, Method, Path>
101+
QueryKey<Prefix, Paths, Method, Path>
93102
>,
94103
"queryKey" | "queryFn"
95104
>,
@@ -101,7 +110,11 @@ export type UseQueryMethod<Paths extends Record<string, Record<HttpMethod, {}>>,
101110
: [InitWithUnknowns<Init>, Options?, QueryClient?]
102111
) => UseQueryResult<InferSelectReturnType<Response["data"], Options["select"]>, Response["error"]>;
103112

104-
export type UseInfiniteQueryMethod<Paths extends Record<string, Record<HttpMethod, {}>>, Media extends MediaType> = <
113+
export type UseInfiniteQueryMethod<
114+
Paths extends Record<string, Record<HttpMethod, {}>>,
115+
Media extends MediaType,
116+
Prefix = unknown,
117+
> = <
105118
Method extends HttpMethod,
106119
Path extends PathsWithMethod<Paths, Method>,
107120
Init extends MaybeOptionalInit<Paths[Path], Method>,
@@ -112,7 +125,7 @@ export type UseInfiniteQueryMethod<Paths extends Record<string, Record<HttpMetho
112125
Response["error"],
113126
InferSelectReturnType<InfiniteData<Response["data"]>, Options["select"]>,
114127
Response["data"],
115-
QueryKey<Paths, Method, Path>,
128+
QueryKey<Prefix, Paths, Method, Path>,
116129
unknown
117130
>,
118131
"queryKey" | "queryFn"
@@ -130,7 +143,11 @@ export type UseInfiniteQueryMethod<Paths extends Record<string, Record<HttpMetho
130143
Response["error"]
131144
>;
132145

133-
export type UseSuspenseQueryMethod<Paths extends Record<string, Record<HttpMethod, {}>>, Media extends MediaType> = <
146+
export type UseSuspenseQueryMethod<
147+
Paths extends Record<string, Record<HttpMethod, {}>>,
148+
Media extends MediaType,
149+
Prefix = unknown,
150+
> = <
134151
Method extends HttpMethod,
135152
Path extends PathsWithMethod<Paths, Method>,
136153
Init extends MaybeOptionalInit<Paths[Path], Method>,
@@ -140,7 +157,7 @@ export type UseSuspenseQueryMethod<Paths extends Record<string, Record<HttpMetho
140157
Response["data"],
141158
Response["error"],
142159
InferSelectReturnType<Response["data"], Options["select"]>,
143-
QueryKey<Paths, Method, Path>
160+
QueryKey<Prefix, Paths, Method, Path>
144161
>,
145162
"queryKey" | "queryFn"
146163
>,
@@ -165,11 +182,11 @@ export type UseMutationMethod<Paths extends Record<string, Record<HttpMethod, {}
165182
queryClient?: QueryClient,
166183
) => UseMutationResult<Response["data"], Response["error"], Init>;
167184

168-
export interface OpenapiQueryClient<Paths extends {}, Media extends MediaType = MediaType> {
169-
queryOptions: QueryOptionsFunction<Paths, Media>;
170-
useQuery: UseQueryMethod<Paths, Media>;
171-
useSuspenseQuery: UseSuspenseQueryMethod<Paths, Media>;
172-
useInfiniteQuery: UseInfiniteQueryMethod<Paths, Media>;
185+
export interface OpenapiQueryClient<Paths extends {}, Media extends MediaType = MediaType, Prefix = unknown> {
186+
queryOptions: QueryOptionsFunction<Paths, Media, Prefix>;
187+
useQuery: UseQueryMethod<Paths, Media, Prefix>;
188+
useSuspenseQuery: UseSuspenseQueryMethod<Paths, Media, Prefix>;
189+
useInfiniteQuery: UseInfiniteQueryMethod<Paths, Media, Prefix>;
173190
useMutation: UseMutationMethod<Paths, Media>;
174191
}
175192

@@ -185,13 +202,14 @@ export type MethodResponse<
185202
: never;
186203

187204
// TODO: Add the ability to bring queryClient as argument
188-
export default function createClient<Paths extends {}, Media extends MediaType = MediaType>(
205+
export default function createClient<Paths extends {}, Media extends MediaType = MediaType, Prefix = unknown>(
189206
client: FetchClient<Paths, Media>,
190-
): OpenapiQueryClient<Paths, Media> {
207+
{ prefixQueryKey }: { prefixQueryKey?: Prefix } = {},
208+
): OpenapiQueryClient<Paths, Media, Prefix> {
191209
const queryFn = async <Method extends HttpMethod, Path extends PathsWithMethod<Paths, Method>>({
192-
queryKey: [method, path, init],
210+
queryKey: [, method, path, init],
193211
signal,
194-
}: QueryFunctionContext<QueryKey<Paths, Method, Path>>) => {
212+
}: QueryFunctionContext<QueryKey<Prefix, Paths, Method, Path>>) => {
195213
const mth = method.toUpperCase() as Uppercase<typeof method>;
196214
const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;
197215
const { data, error, response } = await fn(path, { signal, ...(init as any) }); // TODO: find a way to avoid as any
@@ -205,8 +223,9 @@ export default function createClient<Paths extends {}, Media extends MediaType =
205223
return data;
206224
};
207225

208-
const queryOptions: QueryOptionsFunction<Paths, Media> = (method, path, ...[init, options]) => ({
209-
queryKey: (init === undefined ? ([method, path] as const) : ([method, path, init] as const)) as QueryKey<
226+
const queryOptions: QueryOptionsFunction<Paths, Media, Prefix> = (method, path, ...[init, options]) => ({
227+
queryKey: [prefixQueryKey, method, path, ...(init === undefined ? [] : [init])] as const as QueryKey<
228+
Prefix,
210229
Paths,
211230
typeof method,
212231
typeof path
@@ -227,7 +246,7 @@ export default function createClient<Paths extends {}, Media extends MediaType =
227246
return useInfiniteQuery(
228247
{
229248
queryKey,
230-
queryFn: async ({ queryKey: [method, path, init], pageParam = 0, signal }) => {
249+
queryFn: async ({ queryKey: [, method, path, init], pageParam = 0, signal }) => {
231250
const mth = method.toUpperCase() as Uppercase<typeof method>;
232251
const fn = client[mth] as ClientMethod<Paths, typeof method, Media>;
233252
const mergedInit = {

packages/openapi-react-query/test/index.test.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,35 @@ describe("client", () => {
239239
});
240240
const client = createClient(fetchClient);
241241

242-
expect(client.queryOptions("get", "/foo").queryKey.length).toBe(2);
242+
expect(client.queryOptions("get", "/foo").queryKey.length).toBe(3);
243+
});
244+
245+
it("should differentiate queries by prefixQueryKey", async () => {
246+
const fetchClient1 = createFetchClient<minimalGetPaths>({ baseUrl, fetch: fetchInfinite });
247+
const fetchClient2 = createFetchClient<minimalGetPaths>({ baseUrl, fetch: fetchInfinite });
248+
const client1 = createClient(fetchClient1);
249+
const client11 = createClient(fetchClient1);
250+
const client2 = createClient(fetchClient2, { prefixQueryKey: ["cache2"] as const });
251+
252+
renderHook(
253+
() => {
254+
useQueries({
255+
queries: [
256+
client1.queryOptions("get", "/foo"),
257+
client11.queryOptions("get", "/foo"),
258+
client2.queryOptions("get", "/foo"),
259+
],
260+
});
261+
},
262+
{ wrapper },
263+
);
264+
265+
expectTypeOf(client1.queryOptions("get", "/foo").queryKey[0]).toEqualTypeOf<unknown>();
266+
expectTypeOf(client2.queryOptions("get", "/foo").queryKey[0]).toEqualTypeOf<readonly ["cache2"]>();
267+
268+
// client1 and client11 have the same query key, so 3 - 1 = 2
269+
expect(queryClient.isFetching()).toBe(2);
270+
expect(client2.queryOptions("get", "/foo").queryKey).toEqual([["cache2"], "get", "/foo"]);
243271
});
244272
});
245273

0 commit comments

Comments
 (0)