Skip to content

Commit dd68cce

Browse files
committed
Add infinite query support for pet retrieval by tags
Enhance the OpenAPI configuration to include pagination options and update the App component to utilize both infinite and suspense infinite queries for fetching pets by tags. Introduce new utility functions for infinite query handling in the generated client code.
1 parent 1cd6448 commit dd68cce

File tree

5 files changed

+135
-7
lines changed

5 files changed

+135
-7
lines changed

examples/openapi-ts-tanstack-react-query/openapi-ts.config.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ export default defineConfig({
88
lint: 'eslint',
99
path: './src/client',
1010
},
11+
parser: {
12+
pagination: {
13+
keywords: ['tags', 'limit', 'offset', 'cursor', 'after', 'before'],
14+
},
15+
},
1116
plugins: [
1217
'@hey-api/client-fetch',
1318
'@hey-api/schemas',
@@ -19,6 +24,9 @@ export default defineConfig({
1924
enums: 'javascript',
2025
name: '@hey-api/typescript',
2126
},
22-
'@tanstack/react-query',
27+
{
28+
infiniteQueryOptions: true,
29+
name: '@tanstack/react-query',
30+
},
2331
],
2432
});

examples/openapi-ts-tanstack-react-query/src/App.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,17 @@ import {
1414
Text,
1515
TextField,
1616
} from '@radix-ui/themes';
17-
import { useMutation, useQuery } from '@tanstack/react-query';
17+
import {
18+
useInfiniteQuery,
19+
useMutation,
20+
useQuery,
21+
useSuspenseInfiniteQuery,
22+
} from '@tanstack/react-query';
1823
import { useEffect, useState } from 'react';
1924

2025
import {
2126
addPetMutation,
27+
findPetsByTagsInfiniteOptions,
2228
getPetByIdOptions,
2329
updatePetMutation,
2430
} from './client/@tanstack/react-query.gen';
@@ -90,6 +96,28 @@ function App() {
9096
enabled: Boolean(petId),
9197
});
9298

99+
useInfiniteQuery({
100+
...findPetsByTagsInfiniteOptions({
101+
client: localClient,
102+
query: {
103+
tags: [],
104+
},
105+
}),
106+
getNextPageParam: (lastPage) => lastPage.map(({ name }) => name),
107+
initialPageParam: { query: { tags: [] } },
108+
});
109+
110+
useSuspenseInfiniteQuery({
111+
...findPetsByTagsInfiniteOptions({
112+
client: localClient,
113+
query: {
114+
tags: [],
115+
},
116+
}),
117+
getNextPageParam: (lastPage) => lastPage.map(({ name }) => name),
118+
initialPageParam: { query: { tags: [] } },
119+
});
120+
93121
const onAddPet = async (formData: FormData) => {
94122
// simple form field validation to demonstrate using schemas
95123
if (PetSchema.required.includes('name') && !formData.get('name')) {

examples/openapi-ts-tanstack-react-query/src/client/@tanstack/react-query.gen.ts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import {
44
type DefaultError,
5+
type InfiniteData,
6+
infiniteQueryOptions,
57
queryOptions,
68
type UseMutationOptions,
79
} from '@tanstack/react-query';
@@ -195,6 +197,91 @@ export const findPetsByTagsOptions = (options: Options<FindPetsByTagsData>) =>
195197
queryKey: findPetsByTagsQueryKey(options),
196198
});
197199

200+
const createInfiniteParams = <
201+
K extends Pick<QueryKey<Options>[0], 'body' | 'headers' | 'path' | 'query'>,
202+
>(
203+
queryKey: QueryKey<Options>,
204+
page: K,
205+
) => {
206+
const params = { ...queryKey[0] };
207+
if (page.body) {
208+
params.body = {
209+
...(queryKey[0].body as any),
210+
...(page.body as any),
211+
};
212+
}
213+
if (page.headers) {
214+
params.headers = {
215+
...queryKey[0].headers,
216+
...page.headers,
217+
};
218+
}
219+
if (page.path) {
220+
params.path = {
221+
...(queryKey[0].path as any),
222+
...(page.path as any),
223+
};
224+
}
225+
if (page.query) {
226+
params.query = {
227+
...(queryKey[0].query as any),
228+
...(page.query as any),
229+
};
230+
}
231+
return params as unknown as typeof page;
232+
};
233+
234+
export const findPetsByTagsInfiniteQueryKey = (
235+
options: Options<FindPetsByTagsData>,
236+
): QueryKey<Options<FindPetsByTagsData>> =>
237+
createQueryKey('findPetsByTags', options, true);
238+
239+
/**
240+
* Finds Pets by tags.
241+
*
242+
* Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.
243+
*/
244+
export const findPetsByTagsInfiniteOptions = (
245+
options: Options<FindPetsByTagsData>,
246+
) =>
247+
infiniteQueryOptions<
248+
FindPetsByTagsResponse,
249+
DefaultError,
250+
InfiniteData<FindPetsByTagsResponse>,
251+
QueryKey<Options<FindPetsByTagsData>>,
252+
| Array<string>
253+
| Pick<
254+
QueryKey<Options<FindPetsByTagsData>>[0],
255+
'body' | 'headers' | 'path' | 'query'
256+
>
257+
>({
258+
getNextPageParam: (() => {}) as any,
259+
initialPageParam: {} as any,
260+
queryFn: async ({ pageParam, queryKey, signal }) => {
261+
// @ts-ignore
262+
const page: Pick<
263+
QueryKey<Options<FindPetsByTagsData>>[0],
264+
'body' | 'headers' | 'path' | 'query'
265+
> =
266+
typeof pageParam === 'object'
267+
? pageParam
268+
: {
269+
query: {
270+
tags: pageParam,
271+
},
272+
};
273+
const params = createInfiniteParams(queryKey, page);
274+
const { data } = await Sdk.__registry.get().findPetsByTags({
275+
...options,
276+
...params,
277+
signal,
278+
throwOnError: true,
279+
});
280+
return data;
281+
},
282+
queryKey: findPetsByTagsInfiniteQueryKey(options),
283+
});
284+
198285
/**
199286
* Deletes a pet.
200287
*

packages/openapi-ts/src/plugins/@tanstack/query-core/v5/infiniteQueryOptions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,8 @@ export const createInfiniteQueryOptions = ({
260260
.call(
261261
$.object()
262262
.pretty()
263-
.hint('@ts-ignore')
263+
.prop('initialPageParam', $.object().pretty().as('any'))
264+
.prop('getNextPageParam', $.func().as('any'))
264265
.prop(
265266
'queryFn',
266267
$.func()

packages/openapi-ts/src/ts-dsl/expr/as.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,14 @@ export class AsTsDsl extends Mixed {
3333
}
3434

3535
override toAst() {
36-
return ts.factory.createAsExpression(
37-
this.$node(this.expr),
38-
this.$type(this.type),
39-
);
36+
const exprNode = this.$node(this.expr);
37+
// Wrap function expressions in parentheses to ensure correct precedence
38+
// e.g., (() => {}) as any instead of () => {} as any
39+
const wrappedExpr =
40+
ts.isArrowFunction(exprNode) || ts.isFunctionExpression(exprNode)
41+
? ts.factory.createParenthesizedExpression(exprNode)
42+
: exprNode;
43+
return ts.factory.createAsExpression(wrappedExpr, this.$type(this.type));
4044
}
4145
}
4246

0 commit comments

Comments
 (0)