Skip to content

Commit bfd426b

Browse files
mortiksoartec-lab
andauthored
feat: Add useOperationIdAsQueryKey option for query key generation (#2314)
* feat: Add useOperationIdAsQueryKey option for query key generation This commit adds a new configuration option useOperationIdAsQueryKey to the query options that allows users to generate query keys using the OpenAPI operation ID instead of the route string. Changes Made: - Added useOperationIdAsQueryKey option to QueryOptions and NormalizedQueryOptions interfaces - Modified query key generation logic to use operation ID when enabled - Added support for the new option in normalizeQueryOptions function - Added comprehensive tests for the new functionality Benefits: - Stable keys that dont change when route paths are modified - Cleaner, more semantic query keys - Consistent naming across applications - Backward compatible (defaults to false) Fixes #2096 * docs: Add useOperationIdAsQueryKey documentation and cleanup - Added comprehensive documentation for the useOperationIdAsQueryKey option in the existing output configuration docs - Removed accidental .tool-versions file that's not part of the project structure - Added code examples showing the difference between default and new behavior - Integrated documentation into the existing project structure * fix: add missing entries in options.ts * fix: run yarn format * fix: add props to queryKey if they are part of the path * Update packages/query/src/index.ts Co-authored-by: Shodai Suzuki <info@soartec-lab.work> * Update index.ts Co-authored-by: Shodai Suzuki <info@soartec-lab.work> --------- Co-authored-by: Shodai Suzuki <info@soartec-lab.work>
1 parent 35ed027 commit bfd426b

File tree

6 files changed

+94
-2
lines changed

6 files changed

+94
-2
lines changed

docs/src/pages/reference/configuration/output.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1372,6 +1372,48 @@ Default Value: `false`.
13721372

13731373
Use to make Orval generate query keys as arrays instead of strings.
13741374

1375+
#### useOperationIdAsQueryKey
1376+
1377+
Type: `Boolean`.
1378+
1379+
Default Value: `false`.
1380+
1381+
Use to generate query keys using the OpenAPI operation ID instead of the route path. This provides more stable and semantic query keys that don't change when route paths are modified.
1382+
1383+
**Example:**
1384+
1385+
```js
1386+
module.exports = {
1387+
petstore: {
1388+
output: {
1389+
override: {
1390+
query: {
1391+
useOperationIdAsQueryKey: true,
1392+
},
1393+
},
1394+
},
1395+
},
1396+
};
1397+
```
1398+
1399+
**Generated code comparison:**
1400+
1401+
Default behavior (`useOperationIdAsQueryKey: false`):
1402+
1403+
```typescript
1404+
const getPetByIdQueryKey = (petId: string) => {
1405+
return [`/pets/${petId}`] as const;
1406+
};
1407+
```
1408+
1409+
With `useOperationIdAsQueryKey: true`:
1410+
1411+
```typescript
1412+
const getPetByIdQueryKey = (petId: string) => {
1413+
return ['getPetById', petId] as const;
1414+
};
1415+
```
1416+
13751417
#### version
13761418

13771419
Type: `number`.

packages/core/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,7 @@ export type NormalizedQueryOptions = {
586586
shouldExportHttpClient?: boolean;
587587
shouldExportQueryKey?: boolean;
588588
shouldSplitQueryKey?: boolean;
589+
useOperationIdAsQueryKey?: boolean;
589590
signal?: boolean;
590591
version?: 3 | 4 | 5;
591592
};
@@ -606,6 +607,7 @@ export type QueryOptions = {
606607
shouldExportHttpClient?: boolean;
607608
shouldExportQueryKey?: boolean;
608609
shouldSplitQueryKey?: boolean;
610+
useOperationIdAsQueryKey?: boolean;
609611
signal?: boolean;
610612
version?: 3 | 4 | 5;
611613
};

packages/orval/src/utils/options.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,19 @@ const normalizeQueryOptions = (
739739
: {
740740
signal: globalOptions.signal,
741741
}),
742+
...(isUndefined(globalOptions.useOperationIdAsQueryKey)
743+
? {}
744+
: {
745+
useOperationIdAsQueryKey: globalOptions.useOperationIdAsQueryKey,
746+
}),
747+
...(isUndefined(queryOptions.useOperationIdAsQueryKey)
748+
? {}
749+
: { useOperationIdAsQueryKey: queryOptions.useOperationIdAsQueryKey }),
750+
...(isUndefined(globalOptions.signal)
751+
? {}
752+
: {
753+
signal: globalOptions.signal,
754+
}),
742755
...(isUndefined(queryOptions.signal)
743756
? {}
744757
: { signal: queryOptions.signal }),

packages/query/src/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1328,11 +1328,16 @@ const generateQueryHook = async (
13281328
? getRouteAsArray(route) // Note: this is required for reactivity to work, we will lose it if route params are converted into string, only as array they will be tracked // TODO: add tests for this
13291329
: `\`${route}\``;
13301330

1331+
1332+
// Use operation ID as query key if enabled, otherwise use route string
1333+
const queryKeyIdentifier = override.query.useOperationIdAsQueryKey
1334+
? operationName : routeString;
1335+
13311336
// Note: do not unref() params in Vue - this will make key lose reactivity
13321337
const queryKeyFn = `${
13331338
override.query.shouldExportQueryKey ? 'export ' : ''
13341339
}const ${queryKeyFnName} = (${queryKeyProps}) => {
1335-
return [${routeString}${queryParams ? ', ...(params ? [params]: [])' : ''}${
1340+
return [${queryKeyIdentifier}${queryParams ? ', ...(params ? [params]: [])' : ''}${
13361341
body.implementation ? `, ${body.implementation}` : ''
13371342
}] as const;
13381343
}`;

packages/query/src/utils.test.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { describe, expect, it } from 'vitest';
2-
import { makeRouteSafe, wrapRouteParameters } from './utils';
2+
import {
3+
makeRouteSafe,
4+
normalizeQueryOptions,
5+
wrapRouteParameters,
6+
} from './utils';
37

48
describe('wrapRouteParameters', () => {
59
it('wraps parameters correctly', () => {
@@ -40,3 +44,26 @@ describe('makeRouteSafe', () => {
4044
expect(result).toBe('');
4145
});
4246
});
47+
48+
describe('normalizeQueryOptions', () => {
49+
it('should include useOperationIdAsQueryKey when provided', () => {
50+
const result = normalizeQueryOptions(
51+
{ useOperationIdAsQueryKey: true },
52+
'/workspace',
53+
);
54+
expect(result.useOperationIdAsQueryKey).toBe(true);
55+
});
56+
57+
it('should not include useOperationIdAsQueryKey when false', () => {
58+
const result = normalizeQueryOptions(
59+
{ useOperationIdAsQueryKey: false },
60+
'/workspace',
61+
);
62+
expect(result.useOperationIdAsQueryKey).toBeUndefined();
63+
});
64+
65+
it('should not include useOperationIdAsQueryKey when not provided', () => {
66+
const result = normalizeQueryOptions({}, '/workspace');
67+
expect(result.useOperationIdAsQueryKey).toBeUndefined();
68+
});
69+
});

packages/query/src/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ export const normalizeQueryOptions = (
6060
? { shouldExportHttpClient: true }
6161
: {}),
6262
...(queryOptions.shouldSplitQueryKey ? { shouldSplitQueryKey: true } : {}),
63+
...(queryOptions.useOperationIdAsQueryKey
64+
? { useOperationIdAsQueryKey: true }
65+
: {}),
6366
};
6467
};
6568

0 commit comments

Comments
 (0)