Skip to content

Commit e7b9457

Browse files
committed
feat: add query key function
1 parent abc82b2 commit e7b9457

File tree

8 files changed

+239
-61
lines changed

8 files changed

+239
-61
lines changed

README.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,75 @@ function App() {
160160
export default App;
161161
```
162162

163+
##### Using Mutation hooks
164+
165+
```tsx
166+
// App.tsx
167+
import { usePetServiceAddPet } from "../openapi/queries";
168+
169+
function App() {
170+
const { mutate } = usePetServiceAddPet();
171+
172+
const handleAddPet = () => {
173+
mutate({ name: "Fluffy", status: "available" });
174+
};
175+
176+
return (
177+
<div className="App">
178+
<h1>Add Pet</h1>
179+
<button onClick={handleAddPet}>Add Pet</button>
180+
</div>
181+
);
182+
}
183+
184+
export default App;
185+
```
186+
187+
##### Invalidating queries after mutation
188+
189+
Invalidating queries after a mutation is important to ensure the cache is updated with the new data. This is done by calling the `queryClient.invalidateQueries` function with the query key used by the query hook.
190+
191+
Learn more about invalidating queries [here](https://tanstack.com/query/latest/docs/framework/react/guides/query-invalidation).
192+
193+
To ensure the query key is created the same way as the query hook, you can use the query key function exported by the generated query hooks.
194+
195+
```tsx
196+
import {
197+
usePetServiceFindPetsByStatus,
198+
usePetServiceAddPet,
199+
UsePetServiceFindPetsByStatusKeyFn,
200+
} from "../openapi/queries";
201+
202+
// App.tsx
203+
function App() {
204+
const { data } = usePetServiceFindPetsByStatus({ status: ["available"] });
205+
const { mutate } = usePetServiceAddPet({
206+
onSuccess: () => {
207+
queryClient.invalidateQueries({
208+
// Call the query key function to get the query key, this is important to ensure the query key is created the same way as the query hook, this insures the cache is invalidated correctly and is typed correctly
209+
queryKey: [UsePetServiceFindPetsByStatusKeyFn()],
210+
});
211+
},
212+
});
213+
214+
return (
215+
<div className="App">
216+
<h1>Pet List</h1>
217+
<ul>{data?.map((pet) => <li key={pet.id}>{pet.name}</li>)}</ul>
218+
<button
219+
onClick={() => {
220+
mutate({ name: "Fluffy", status: "available" });
221+
}}
222+
>
223+
Add Pet
224+
</button>
225+
</div>
226+
);
227+
}
228+
229+
export default App;
230+
```
231+
163232
##### Runtime Configuration
164233

165234
You can modify the default values used by the generated service calls by modifying the OpenAPI configuration singleton object.

examples/react-app/src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import "./App.css";
22
import {
33
useDefaultServiceAddPet,
44
useDefaultServiceFindPets,
5-
useDefaultServiceFindPetsKey,
5+
UseDefaultServiceFindPetsKeyFn,
66
useDefaultServiceGetNotDefined,
77
useDefaultServicePostNotDefined,
88
} from "../openapi/queries";
@@ -54,7 +54,7 @@ function App() {
5454
{
5555
onSuccess: () => {
5656
queryClient.invalidateQueries({
57-
queryKey: [useDefaultServiceFindPetsKey],
57+
queryKey: UseDefaultServiceFindPetsKeyFn(),
5858
});
5959
console.log("success");
6060
},

src/common.mts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ export const TData = ts.factory.createIdentifier("TData");
1515
export const TError = ts.factory.createIdentifier("TError");
1616
export const TContext = ts.factory.createIdentifier("TContext");
1717

18+
export const EqualsOrGreaterThanToken = ts.factory.createToken(
19+
ts.SyntaxKind.EqualsGreaterThanToken
20+
);
21+
22+
export const QuestionToken = ts.factory.createToken(
23+
ts.SyntaxKind.QuestionToken
24+
);
25+
1826
export const queryKeyGenericType =
1927
ts.factory.createTypeReferenceNode("TQueryKey");
2028
export const queryKeyConstraint = ts.factory.createTypeReferenceNode("Array", [

src/createExports.mts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,12 @@ export const createExports = (service: Service) => {
4040
];
4141

4242
const commonInQueries = allQueries
43-
.map(({ apiResponse, returnType, key }) => [apiResponse, returnType, key])
43+
.map(({ apiResponse, returnType, key, queryKeyFn }) => [
44+
apiResponse,
45+
returnType,
46+
key,
47+
queryKeyFn,
48+
])
4449
.flat();
4550
const commonInMutations = allMutations
4651
.map(({ mutationResult }) => [mutationResult])

src/createUseQuery.mts

Lines changed: 118 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import { MethodDeclaration } from "ts-morph";
33
import {
44
BuildCommonTypeName,
55
capitalizeFirstLetter,
6+
EqualsOrGreaterThanToken,
67
extractPropertiesFromObjectParam,
78
getNameFromMethod,
89
getShortType,
910
queryKeyConstraint,
1011
queryKeyGenericType,
12+
QuestionToken,
1113
TData,
1214
TError,
1315
} from "./common.mjs";
@@ -61,12 +63,15 @@ export const createApiResponseType = ({
6163
);
6264

6365
return {
64-
/** DefaultResponseDataType
66+
/**
67+
* DefaultResponseDataType
68+
*
6569
* export type MyClassMethodDefaultResponse = Awaited<ReturnType<typeof myClass.myMethod>>
6670
*/
6771
apiResponse,
6872
/**
69-
* will be the name of the type of the response type of the method
73+
* This will be the name of the type of the response type of the method
74+
*
7075
* MyClassMethodDefaultResponse
7176
*/
7277
responseDataType,
@@ -128,6 +133,7 @@ export function getRequestParamFromMethod(method: MethodDeclaration) {
128133

129134
/**
130135
* Return Type
136+
*
131137
* export const classNameMethodNameQueryResult<TData = MyClassMethodDefaultResponse, TError = unknown> = UseQueryResult<TData, TError>;
132138
*/
133139
export function createReturnTypeExport({
@@ -311,7 +317,7 @@ export function createQueryHook({
311317
),
312318
],
313319
undefined,
314-
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
320+
EqualsOrGreaterThanToken,
315321
ts.factory.createCallExpression(
316322
ts.factory.createIdentifier(queryString),
317323
[
@@ -322,41 +328,28 @@ export function createQueryHook({
322328
ts.factory.createObjectLiteralExpression([
323329
ts.factory.createPropertyAssignment(
324330
ts.factory.createIdentifier("queryKey"),
325-
ts.factory.createArrayLiteralExpression(
326-
[
327-
BuildCommonTypeName(queryKey),
328-
ts.factory.createSpreadElement(
329-
ts.factory.createParenthesizedExpression(
330-
ts.factory.createBinaryExpression(
331-
ts.factory.createIdentifier("queryKey"),
332-
ts.factory.createToken(
333-
ts.SyntaxKind.QuestionQuestionToken
334-
),
335-
method.getParameters().length
336-
? ts.factory.createArrayLiteralExpression([
337-
ts.factory.createObjectLiteralExpression(
338-
method
339-
.getParameters()
340-
.map((param) =>
341-
extractPropertiesFromObjectParam(
342-
param
343-
).map((p) =>
344-
ts.factory.createShorthandPropertyAssignment(
345-
ts.factory.createIdentifier(
346-
p.name
347-
)
348-
)
349-
)
350-
)
351-
.flat()
352-
),
353-
])
354-
: ts.factory.createArrayLiteralExpression([])
355-
)
356-
)
357-
),
358-
],
359-
false
331+
ts.factory.createCallExpression(
332+
BuildCommonTypeName(getQueryKeyFnName(queryKey)),
333+
undefined,
334+
335+
method.getParameters().length
336+
? [
337+
ts.factory.createObjectLiteralExpression(
338+
method
339+
.getParameters()
340+
.map((param) =>
341+
extractPropertiesFromObjectParam(param).map(
342+
(p) =>
343+
ts.factory.createShorthandPropertyAssignment(
344+
ts.factory.createIdentifier(p.name)
345+
)
346+
)
347+
)
348+
.flat()
349+
),
350+
ts.factory.createIdentifier("queryKey"),
351+
]
352+
: []
360353
)
361354
),
362355
ts.factory.createPropertyAssignment(
@@ -366,9 +359,7 @@ export function createQueryHook({
366359
undefined,
367360
[],
368361
undefined,
369-
ts.factory.createToken(
370-
ts.SyntaxKind.EqualsGreaterThanToken
371-
),
362+
EqualsOrGreaterThanToken,
372363
ts.factory.createAsExpression(
373364
ts.factory.createCallExpression(
374365
ts.factory.createPropertyAccessExpression(
@@ -463,11 +454,97 @@ export const createUseQuery = ({
463454
queryKey,
464455
});
465456

457+
const queryKeyFn = createQueryKeyFnExport(queryKey, method);
458+
466459
return {
467460
apiResponse: defaultApiResponse,
468461
returnType: returnTypeExport,
469462
key: queryKeyExport,
470463
queryHook: hookWithJsDoc,
471464
suspenseQueryHook: suspenseHookWithJsDoc,
465+
queryKeyFn,
472466
};
473467
};
468+
469+
function getQueryKeyFnName(queryKey: string) {
470+
return `${capitalizeFirstLetter(queryKey)}Fn`;
471+
}
472+
473+
function createQueryKeyFnExport(queryKey: string, method: MethodDeclaration) {
474+
const params = getRequestParamFromMethod(method);
475+
476+
// override key is used to allow the user to override the the queryKey values
477+
const overrideKey = ts.factory.createParameterDeclaration(
478+
undefined,
479+
undefined,
480+
ts.factory.createIdentifier("queryKey"),
481+
QuestionToken,
482+
ts.factory.createTypeReferenceNode("Array<unknown>", [])
483+
);
484+
485+
return ts.factory.createVariableStatement(
486+
[ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
487+
ts.factory.createVariableDeclarationList(
488+
[
489+
ts.factory.createVariableDeclaration(
490+
ts.factory.createIdentifier(getQueryKeyFnName(queryKey)),
491+
undefined,
492+
undefined,
493+
ts.factory.createArrowFunction(
494+
undefined,
495+
undefined,
496+
params ? [params, overrideKey] : [],
497+
undefined,
498+
EqualsOrGreaterThanToken,
499+
queryKeyFn(queryKey, method)
500+
)
501+
),
502+
],
503+
ts.NodeFlags.Const
504+
)
505+
);
506+
}
507+
508+
function queryKeyFn(
509+
queryKey: string,
510+
method: MethodDeclaration
511+
): ts.Expression {
512+
const params = getRequestParamFromMethod(method);
513+
514+
if (!params) {
515+
return ts.factory.createArrayLiteralExpression([
516+
ts.factory.createIdentifier(queryKey),
517+
]);
518+
}
519+
520+
return ts.factory.createArrayLiteralExpression(
521+
[
522+
ts.factory.createIdentifier(queryKey),
523+
ts.factory.createSpreadElement(
524+
ts.factory.createParenthesizedExpression(
525+
ts.factory.createBinaryExpression(
526+
ts.factory.createIdentifier("queryKey"),
527+
ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
528+
method.getParameters().length
529+
? ts.factory.createArrayLiteralExpression([
530+
ts.factory.createObjectLiteralExpression(
531+
method
532+
.getParameters()
533+
.map((param) =>
534+
extractPropertiesFromObjectParam(param).map((p) =>
535+
ts.factory.createShorthandPropertyAssignment(
536+
ts.factory.createIdentifier(p.name)
537+
)
538+
)
539+
)
540+
.flat()
541+
),
542+
])
543+
: ts.factory.createArrayLiteralExpression([])
544+
)
545+
)
546+
),
547+
],
548+
false
549+
);
550+
}

0 commit comments

Comments
 (0)