Skip to content

Commit 42c478a

Browse files
authored
feat: expose query client functions directly in react query client (#227)
1 parent b087cab commit 42c478a

5 files changed

Lines changed: 500 additions & 255 deletions

File tree

.changeset/grumpy-games-play.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@ts-rest/react-query': patch
3+
---
4+
5+
Provide QueryClient functions through client directly

apps/docs/docs/react-query.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,8 @@ Essentially, the API is exactly the same, but instead of passing a `queryFn`, yo
265265
- `getQueriesData(filters) => [QueryKey, SuccessResponse | undefined][]`
266266
- `setQueryData(queryKey, updater) => SuccessResponse | undefined`
267267

268+
If you would like to use any of these functions outside a component or a hook, these functions are also available through the client directly, but you have to pass in the `QueryClient` instance as the first argument.
269+
268270
Functions such as `invalidateQueries` that neither exchange typed data nor invoke the API have no benefit of being wrapped. Therefore, call these functions directly through your `QueryClient` instance.
269271

270272
:::info

apps/example-next/tests/react-query.spec.tsx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
1+
import {
2+
QueryClient,
3+
QueryClientProvider,
4+
useQueryClient,
5+
} from '@tanstack/react-query';
26
import { waitFor } from '@testing-library/react';
37
import { renderHook } from '@testing-library/react-hooks';
48
import { ApiFetcher, initContract } from '@ts-rest/core';
@@ -624,6 +628,34 @@ describe('react-query', () => {
624628
it('fetchQuery should handle success', async () => {
625629
api.mockResolvedValue(SUCCESS_RESPONSE);
626630

631+
renderHook(
632+
() => {
633+
const queryClient = useQueryClient();
634+
return client.posts.getPost.fetchQuery(queryClient, ['post', '1'], {
635+
params: {
636+
id: '1',
637+
},
638+
});
639+
},
640+
{
641+
wrapper,
642+
}
643+
);
644+
645+
expect(api).toHaveBeenCalledWith({
646+
method: 'GET',
647+
path: 'https://api.com/posts/1',
648+
body: undefined,
649+
headers: {
650+
'content-type': 'application/json',
651+
'x-test': 'test',
652+
},
653+
});
654+
});
655+
656+
it('fetchQuery should handle success hook', async () => {
657+
api.mockResolvedValue(SUCCESS_RESPONSE);
658+
627659
renderHook(
628660
() => {
629661
const apiQueryClient = useTsRestQueryClient(client);
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
import {
2+
AppRoute,
3+
AppRouteFunction,
4+
AppRouteMutation,
5+
AppRouteQuery,
6+
AreAllPropertiesOptional,
7+
ClientArgs,
8+
Prettify,
9+
Without,
10+
} from '@ts-rest/core';
11+
import {
12+
DataResponse,
13+
DataReturnArgs,
14+
UseInfiniteQueryOptions,
15+
UseInfiniteQueryResult,
16+
UseMutationOptions,
17+
UseMutationResult,
18+
UseQueryOptions,
19+
UseQueryResult,
20+
} from './types';
21+
import {
22+
FetchQueryOptions,
23+
InfiniteData,
24+
QueryClient,
25+
QueryFilters,
26+
QueryFunctionContext,
27+
QueryKey,
28+
} from '@tanstack/react-query';
29+
30+
export type AppRouteFunctions<
31+
TAppRoute extends AppRoute,
32+
TClientArgs extends ClientArgs
33+
> = {
34+
useQuery: TAppRoute extends AppRouteQuery
35+
? DataReturnQuery<TAppRoute, TClientArgs>
36+
: never;
37+
useInfiniteQuery: TAppRoute extends AppRouteQuery
38+
? DataReturnInfiniteQuery<TAppRoute, TClientArgs>
39+
: never;
40+
useQueries: TAppRoute extends AppRouteQuery
41+
? DataReturnQueries<TAppRoute, TClientArgs>
42+
: never;
43+
query: TAppRoute extends AppRouteQuery
44+
? AppRouteFunction<TAppRoute, TClientArgs>
45+
: never;
46+
useMutation: TAppRoute extends AppRouteMutation
47+
? DataReturnMutation<TAppRoute, TClientArgs>
48+
: never;
49+
mutation: TAppRoute extends AppRouteMutation
50+
? AppRouteFunction<TAppRoute, TClientArgs>
51+
: never;
52+
fetchQuery: TAppRoute extends AppRouteQuery
53+
? DataReturnFetchQuery<TAppRoute, TClientArgs>
54+
: never;
55+
fetchInfiniteQuery: TAppRoute extends AppRouteQuery
56+
? DataReturnFetchInfiniteQuery<TAppRoute, TClientArgs>
57+
: never;
58+
prefetchQuery: TAppRoute extends AppRouteQuery
59+
? DataReturnPrefetchQuery<TAppRoute, TClientArgs>
60+
: never;
61+
prefetchInfiniteQuery: TAppRoute extends AppRouteQuery
62+
? DataReturnPrefetchInfiniteQuery<TAppRoute, TClientArgs>
63+
: never;
64+
getQueryData: TAppRoute extends AppRouteQuery
65+
? DataReturnGetQueryData<TAppRoute>
66+
: never;
67+
ensureQueryData: TAppRoute extends AppRouteQuery
68+
? DataReturnFetchQuery<TAppRoute, TClientArgs>
69+
: never;
70+
getQueriesData: TAppRoute extends AppRouteQuery
71+
? DataReturnGetQueriesData<TAppRoute>
72+
: never;
73+
setQueryData: TAppRoute extends AppRouteQuery
74+
? DataReturnSetQueryData<TAppRoute>
75+
: never;
76+
};
77+
78+
export type AppRouteFunctionsWithQueryClient<
79+
TAppRoute extends AppRoute,
80+
TClientArgs extends ClientArgs
81+
> = AppRouteFunctions<TAppRoute, TClientArgs> & {
82+
fetchQuery: TAppRoute extends AppRouteQuery
83+
? DataReturnFetchQueryHook<TAppRoute, TClientArgs>
84+
: never;
85+
fetchInfiniteQuery: TAppRoute extends AppRouteQuery
86+
? DataReturnFetchInfiniteQueryHook<TAppRoute, TClientArgs>
87+
: never;
88+
prefetchQuery: TAppRoute extends AppRouteQuery
89+
? DataReturnPrefetchQueryHook<TAppRoute, TClientArgs>
90+
: never;
91+
prefetchInfiniteQuery: TAppRoute extends AppRouteQuery
92+
? DataReturnPrefetchInfiniteQueryHook<TAppRoute, TClientArgs>
93+
: never;
94+
getQueryData: TAppRoute extends AppRouteQuery
95+
? DataReturnGetQueryDataHook<TAppRoute>
96+
: never;
97+
ensureQueryData: TAppRoute extends AppRouteQuery
98+
? DataReturnFetchQueryHook<TAppRoute, TClientArgs>
99+
: never;
100+
getQueriesData: TAppRoute extends AppRouteQuery
101+
? DataReturnGetQueriesDataHook<TAppRoute>
102+
: never;
103+
setQueryData: TAppRoute extends AppRouteQuery
104+
? DataReturnSetQueryDataHook<TAppRoute>
105+
: never;
106+
};
107+
108+
// Used on X.useQuery
109+
export type DataReturnQuery<
110+
TAppRoute extends AppRoute,
111+
TClientArgs extends ClientArgs,
112+
TArgs = Prettify<Without<DataReturnArgs<TAppRoute, TClientArgs>, never>>
113+
> = AreAllPropertiesOptional<TArgs> extends true
114+
? (
115+
queryKey: QueryKey,
116+
args?: TArgs,
117+
options?: UseQueryOptions<TAppRoute>
118+
) => UseQueryResult<TAppRoute>
119+
: (
120+
queryKey: QueryKey,
121+
args: TArgs,
122+
options?: UseQueryOptions<TAppRoute>
123+
) => UseQueryResult<TAppRoute>;
124+
125+
export type DataReturnQueriesOptions<
126+
TAppRoute extends AppRoute,
127+
TClientArgs extends ClientArgs
128+
> = Without<DataReturnArgs<TAppRoute, TClientArgs>, never> &
129+
Omit<UseQueryOptions<TAppRoute>, 'queryFn'> & {
130+
queryKey: QueryKey;
131+
};
132+
133+
export type DataReturnQueries<
134+
TAppRoute extends AppRoute,
135+
TClientArgs extends ClientArgs,
136+
TQueries = readonly DataReturnQueriesOptions<TAppRoute, TClientArgs>[]
137+
> = (args: {
138+
queries: TQueries;
139+
context?: UseQueryOptions<TAppRoute>['context'];
140+
}) => UseQueryResult<TAppRoute>[];
141+
142+
// Used on X.useInfiniteQuery
143+
export type DataReturnInfiniteQuery<
144+
TAppRoute extends AppRoute,
145+
TClientArgs extends ClientArgs
146+
> = (
147+
queryKey: QueryKey,
148+
args: (
149+
context: QueryFunctionContext<QueryKey>
150+
) => Without<DataReturnArgs<TAppRoute, TClientArgs>, never>,
151+
options?: UseInfiniteQueryOptions<TAppRoute>
152+
) => UseInfiniteQueryResult<TAppRoute>;
153+
154+
// Used pn X.useMutation
155+
export type DataReturnMutation<
156+
TAppRoute extends AppRoute,
157+
TClientArgs extends ClientArgs
158+
> = (
159+
options?: UseMutationOptions<TAppRoute, TClientArgs>
160+
) => UseMutationResult<TAppRoute, TClientArgs>;
161+
162+
export type DataReturnFetchQuery<
163+
TAppRoute extends AppRoute,
164+
TClientArgs extends ClientArgs,
165+
TArgs = Prettify<Without<DataReturnArgs<TAppRoute, TClientArgs>, never>>
166+
> = AreAllPropertiesOptional<TArgs> extends true
167+
? (
168+
queryClient: QueryClient,
169+
queryKey: QueryKey,
170+
args?: TArgs,
171+
options?: FetchQueryOptions<TAppRoute>
172+
) => Promise<DataResponse<TAppRoute>>
173+
: (
174+
queryClient: QueryClient,
175+
queryKey: QueryKey,
176+
args: TArgs,
177+
options?: FetchQueryOptions<TAppRoute>
178+
) => Promise<DataResponse<TAppRoute>>;
179+
180+
export type DataReturnFetchQueryHook<
181+
TAppRoute extends AppRoute,
182+
TClientArgs extends ClientArgs,
183+
TArgs = Prettify<Without<DataReturnArgs<TAppRoute, TClientArgs>, never>>
184+
> = AreAllPropertiesOptional<TArgs> extends true
185+
? (
186+
queryKey: QueryKey,
187+
args?: TArgs,
188+
options?: FetchQueryOptions<TAppRoute>
189+
) => Promise<DataResponse<TAppRoute>>
190+
: (
191+
queryKey: QueryKey,
192+
args: TArgs,
193+
options?: FetchQueryOptions<TAppRoute>
194+
) => Promise<DataResponse<TAppRoute>>;
195+
196+
export type DataReturnPrefetchQuery<
197+
TAppRoute extends AppRoute,
198+
TClientArgs extends ClientArgs,
199+
TArgs = Prettify<Without<DataReturnArgs<TAppRoute, TClientArgs>, never>>
200+
> = AreAllPropertiesOptional<TArgs> extends true
201+
? (
202+
queryClient: QueryClient,
203+
queryKey: QueryKey,
204+
args?: TArgs,
205+
options?: FetchQueryOptions<TAppRoute>
206+
) => Promise<void>
207+
: (
208+
queryClient: QueryClient,
209+
queryKey: QueryKey,
210+
args: TArgs,
211+
options?: FetchQueryOptions<TAppRoute>
212+
) => Promise<void>;
213+
214+
export type DataReturnPrefetchQueryHook<
215+
TAppRoute extends AppRoute,
216+
TClientArgs extends ClientArgs,
217+
TArgs = Prettify<Without<DataReturnArgs<TAppRoute, TClientArgs>, never>>
218+
> = AreAllPropertiesOptional<TArgs> extends true
219+
? (
220+
queryKey: QueryKey,
221+
args?: TArgs,
222+
options?: FetchQueryOptions<TAppRoute>
223+
) => Promise<void>
224+
: (
225+
queryKey: QueryKey,
226+
args: TArgs,
227+
options?: FetchQueryOptions<TAppRoute>
228+
) => Promise<void>;
229+
230+
export type DataReturnFetchInfiniteQuery<
231+
TAppRoute extends AppRoute,
232+
TClientArgs extends ClientArgs,
233+
TArgs = Prettify<Without<DataReturnArgs<TAppRoute, TClientArgs>, never>>
234+
> = (
235+
queryClient: QueryClient,
236+
queryKey: QueryKey,
237+
args: (context: QueryFunctionContext) => TArgs,
238+
options?: FetchQueryOptions<TAppRoute>
239+
) => Promise<InfiniteData<DataResponse<TAppRoute>>>;
240+
241+
export type DataReturnFetchInfiniteQueryHook<
242+
TAppRoute extends AppRoute,
243+
TClientArgs extends ClientArgs,
244+
TArgs = Prettify<Without<DataReturnArgs<TAppRoute, TClientArgs>, never>>
245+
> = (
246+
queryKey: QueryKey,
247+
args: (context: QueryFunctionContext) => TArgs,
248+
options?: FetchQueryOptions<TAppRoute>
249+
) => Promise<InfiniteData<DataResponse<TAppRoute>>>;
250+
251+
export type DataReturnPrefetchInfiniteQuery<
252+
TAppRoute extends AppRoute,
253+
TClientArgs extends ClientArgs,
254+
TArgs = Prettify<Without<DataReturnArgs<TAppRoute, TClientArgs>, never>>
255+
> = (
256+
queryClient: QueryClient,
257+
queryKey: QueryKey,
258+
args: (context: QueryFunctionContext) => TArgs,
259+
options?: FetchQueryOptions<TAppRoute>
260+
) => Promise<void>;
261+
262+
export type DataReturnPrefetchInfiniteQueryHook<
263+
TAppRoute extends AppRoute,
264+
TClientArgs extends ClientArgs,
265+
TArgs = Prettify<Without<DataReturnArgs<TAppRoute, TClientArgs>, never>>
266+
> = (
267+
queryKey: QueryKey,
268+
args: (context: QueryFunctionContext) => TArgs,
269+
options?: FetchQueryOptions<TAppRoute>
270+
) => Promise<void>;
271+
272+
export type DataReturnGetQueryData<TAppRoute extends AppRoute> = (
273+
queryClient: QueryClient,
274+
queryKey: QueryKey,
275+
filters?: QueryFilters
276+
) => DataResponse<TAppRoute> | undefined;
277+
278+
export type DataReturnGetQueryDataHook<TAppRoute extends AppRoute> = (
279+
queryKey: QueryKey,
280+
filters?: QueryFilters
281+
) => DataResponse<TAppRoute> | undefined;
282+
283+
export type DataReturnGetQueriesData<TAppRoute extends AppRoute> = (
284+
queryClient: QueryClient,
285+
filters: QueryFilters
286+
) => [queryKey: QueryKey, data: DataResponse<TAppRoute> | undefined][];
287+
288+
export type DataReturnGetQueriesDataHook<TAppRoute extends AppRoute> = (
289+
filters: QueryFilters
290+
) => [queryKey: QueryKey, data: DataResponse<TAppRoute> | undefined][];
291+
292+
export type DataReturnSetQueryData<TAppRoute extends AppRoute> = (
293+
queryClient: QueryClient,
294+
queryKey: QueryKey,
295+
updater:
296+
| DataResponse<TAppRoute>
297+
| undefined
298+
| ((
299+
oldData: DataResponse<TAppRoute> | undefined
300+
) => DataResponse<TAppRoute> | undefined)
301+
) => DataResponse<TAppRoute> | undefined;
302+
303+
export type DataReturnSetQueryDataHook<TAppRoute extends AppRoute> = (
304+
queryKey: QueryKey,
305+
updater:
306+
| DataResponse<TAppRoute>
307+
| undefined
308+
| ((
309+
oldData: DataResponse<TAppRoute> | undefined
310+
) => DataResponse<TAppRoute> | undefined)
311+
) => DataResponse<TAppRoute> | undefined;

0 commit comments

Comments
 (0)