@kubb/plugin-react-query 5.0.0-alpha.9 → 5.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +34 -85
  3. package/dist/components-Dow6tde8.js +1459 -0
  4. package/dist/components-Dow6tde8.js.map +1 -0
  5. package/dist/components-HwdCDefj.cjs +1603 -0
  6. package/dist/components-HwdCDefj.cjs.map +1 -0
  7. package/dist/components.cjs +1 -1
  8. package/dist/components.d.ts +49 -179
  9. package/dist/components.js +1 -1
  10. package/dist/generators-CcOmnTPa.cjs +1454 -0
  11. package/dist/generators-CcOmnTPa.cjs.map +1 -0
  12. package/dist/generators-yfZr_qfT.js +1412 -0
  13. package/dist/generators-yfZr_qfT.js.map +1 -0
  14. package/dist/generators.cjs +1 -1
  15. package/dist/generators.d.ts +9 -505
  16. package/dist/generators.js +1 -1
  17. package/dist/index.cjs +197 -126
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.ts +4 -4
  20. package/dist/index.js +193 -126
  21. package/dist/index.js.map +1 -1
  22. package/dist/types-DG_OxOym.d.ts +363 -0
  23. package/extension.yaml +911 -0
  24. package/package.json +59 -64
  25. package/src/components/InfiniteQuery.tsx +79 -138
  26. package/src/components/InfiniteQueryOptions.tsx +55 -166
  27. package/src/components/Mutation.tsx +74 -111
  28. package/src/components/MutationOptions.tsx +61 -80
  29. package/src/components/Query.tsx +66 -142
  30. package/src/components/QueryOptions.tsx +56 -138
  31. package/src/components/SuspenseInfiniteQuery.tsx +79 -138
  32. package/src/components/SuspenseInfiniteQueryOptions.tsx +55 -166
  33. package/src/components/SuspenseQuery.tsx +66 -152
  34. package/src/generators/customHookOptionsFileGenerator.tsx +37 -51
  35. package/src/generators/hookOptionsGenerator.tsx +111 -174
  36. package/src/generators/infiniteQueryGenerator.tsx +158 -178
  37. package/src/generators/mutationGenerator.tsx +112 -139
  38. package/src/generators/queryGenerator.tsx +128 -142
  39. package/src/generators/suspenseInfiniteQueryGenerator.tsx +157 -156
  40. package/src/generators/suspenseQueryGenerator.tsx +126 -152
  41. package/src/index.ts +1 -1
  42. package/src/plugin.ts +134 -187
  43. package/src/resolvers/resolverReactQuery.ts +107 -0
  44. package/src/types.ts +172 -49
  45. package/src/utils.ts +10 -0
  46. package/dist/components-BHQT9ZLc.cjs +0 -1634
  47. package/dist/components-BHQT9ZLc.cjs.map +0 -1
  48. package/dist/components-CpyHYGOw.js +0 -1520
  49. package/dist/components-CpyHYGOw.js.map +0 -1
  50. package/dist/generators-DP07m3rH.cjs +0 -1469
  51. package/dist/generators-DP07m3rH.cjs.map +0 -1
  52. package/dist/generators-DkQwKTc2.js +0 -1427
  53. package/dist/generators-DkQwKTc2.js.map +0 -1
  54. package/dist/types-D5S7Ny9r.d.ts +0 -270
package/extension.yaml ADDED
@@ -0,0 +1,911 @@
1
+ $schema: https://kubb.dev/schemas/extension.json
2
+ kind: plugin
3
+ id: plugin-react-query
4
+ name: React Query
5
+ description: Generate React Query hooks (useQuery, useMutation) from OpenAPI specifications.
6
+ category: framework
7
+ type: official
8
+ npmPackage: '@kubb/plugin-react-query'
9
+ docsPath: /plugins/plugin-react-query
10
+ repo: https://github.com/kubb-labs/plugins
11
+ maintainers:
12
+ - name: Stijn Van Hulle
13
+ github: stijnvanhulle
14
+ compatibility:
15
+ kubb: '>=5.0.0'
16
+ node: '>=22'
17
+ tags:
18
+ - react-query
19
+ - tanstack-query
20
+ - react
21
+ - hooks
22
+ - data-fetching
23
+ - codegen
24
+ - openapi
25
+ dependencies:
26
+ - plugin-ts
27
+ - plugin-client
28
+ resources:
29
+ documentation: https://kubb.dev/plugins/plugin-react-query
30
+ repository: https://github.com/kubb-labs/plugins
31
+ issues: https://github.com/kubb-labs/plugins/issues
32
+ changelog: https://github.com/kubb-labs/plugins/blob/main/packages/plugin-react-query/CHANGELOG.md
33
+ codesandbox: https://codesandbox.io/p/github/kubb-labs/plugins/main/examples/react-query
34
+ featured: true
35
+ icon:
36
+ light: https://kubb.dev/feature/tanstack.svg
37
+ intro: |
38
+ # @kubb/plugin-react-query
39
+
40
+ Generate type-safe React Query hooks from your OpenAPI schema for data fetching, caching, and synchronization.
41
+ options:
42
+ - name: output
43
+ type: Output
44
+ required: false
45
+ default: "{ path: 'hooks', barrel: { type: 'named' } }"
46
+ description: Specify the export location for the files and define the behavior of the output.
47
+ properties:
48
+ - name: path
49
+ type: string
50
+ required: true
51
+ description: Output directory or file for the generated code, relative to the global `output.path`.
52
+ tip: |
53
+ if `output.path` is a file, `group` cannot be used.
54
+ default: "'hooks'"
55
+ - name: barrel
56
+ type: "{ type: 'named' | 'all', nested?: boolean } | false"
57
+ required: false
58
+ default: "{ type: 'named' }"
59
+ description: "Configure barrel file export strategy. Use `type` to control named vs. wildcard exports; set `nested: true` to generate hierarchical barrels in subdirectories."
60
+ examples:
61
+ - name: all
62
+ files:
63
+ - lang: typescript
64
+ code: |
65
+ export * from './gen/petService.ts'
66
+ twoslash: false
67
+ - name: named
68
+ files:
69
+ - lang: typescript
70
+ code: |
71
+ export { PetService } from './gen/petService.ts'
72
+ twoslash: false
73
+ - name: 'false'
74
+ files:
75
+ - lang: typescript
76
+ code: ''
77
+ twoslash: false
78
+ - name: banner
79
+ type: 'string | ((node: RootNode) => string)'
80
+ required: false
81
+ description: Add a banner comment at the top of every generated file. Accepts a static string or a function that receives the `RootNode` and returns a string.
82
+ - name: footer
83
+ type: 'string | ((node: RootNode) => string)'
84
+ required: false
85
+ description: Add a footer comment at the end of every generated file. Accepts a static string or a function that receives the `RootNode` and returns a string.
86
+ - name: override
87
+ type: boolean
88
+ required: false
89
+ default: 'false'
90
+ description: Whether Kubb overrides existing external files that can be generated if they already exist.
91
+ - name: group
92
+ type: Group
93
+ required: false
94
+ description: |
95
+ Grouping combines files in a folder based on a specific `type`.
96
+ examples:
97
+ - name: kubb.config.ts
98
+ files:
99
+ - lang: typescript
100
+ code: |
101
+ group: {
102
+ type: 'tag',
103
+ name({ group }) {
104
+ return `${group}Controller`
105
+ }
106
+ }
107
+ twoslash: false
108
+ body: |
109
+ With the configuration above, the generator emits:
110
+
111
+ ```text
112
+ .
113
+ ├── src/
114
+ │ └── petController/
115
+ │ │ ├── addPet.ts
116
+ │ │ └── getPet.ts
117
+ │ └── storeController/
118
+ │ ├── createStore.ts
119
+ │ └── getStoreById.ts
120
+ ├── petStore.yaml
121
+ ├── kubb.config.ts
122
+ └── package.json
123
+ ```
124
+ properties:
125
+ - name: type
126
+ type: "'tag'"
127
+ required: true
128
+ description: Specify the property to group files by. Required when `group` is defined.
129
+ note: |
130
+ `Required: true*` means this is required only when the `group` option is used. The `group` option itself is optional.
131
+ body: |
132
+ - `'tag'`: Uses the first tag from `operation.getTags().at(0)?.name`
133
+ - name: name
134
+ type: '(context: GroupContext) => string'
135
+ required: false
136
+ default: (ctx) => `${ctx.group}Controller`
137
+ description: Return the name of a group based on the group name. This is used for file and identifier generation.
138
+ - name: client
139
+ type: ClientImportPath & { clientType?, dataReturnType?, baseURL?, bundle? }
140
+ required: false
141
+ description: Client configuration for HTTP request generation.
142
+ properties:
143
+ - name: importPath
144
+ type: string
145
+ required: false
146
+ description: Path to the client used for API calls. Supports both relative and absolute paths.
147
+ details:
148
+ - title: When to use `importPath`
149
+ body: |
150
+ Use `importPath` when you want to:
151
+
152
+ - **Customize the HTTP client**: Provide your own client implementation with custom configurations (e.g., baseURL, headers, interceptors)
153
+ - **Add authentication**: Include authentication tokens or other security mechanisms in your client
154
+ - **Override default behavior**: Replace the default Kubb client with your own implementation
155
+ - title: Default behavior
156
+ body: |
157
+ When `importPath` is not specified:
158
+
159
+ - If `bundle: false` (default): Uses `@kubb/plugin-client/clients/${client}` where client is either `axios` or `fetch`.
160
+ - If `bundle: true`: Bundles the client into `.kubb/fetch.ts`.
161
+ - title: Import structure
162
+ body: 'Generated code imports the client as a default import and the runtime types as named type imports:'
163
+ codeBlock:
164
+ lang: typescript
165
+ code: |-
166
+ /**
167
+ * Generated by Kubb (https://kubb.dev/).
168
+ * Do not edit manually.
169
+ */
170
+ import client from '${client.importPath}'
171
+ import type { RequestConfig, ResponseErrorConfig } from '${client.importPath}'
172
+ // ... rest of generated file
173
+ important: |
174
+ When using `importPath` with query plugins such as `@kubb/plugin-react-query` or `@kubb/plugin-vue-query`, the generated hooks also import a `Client` type alongside `RequestConfig` and `ResponseErrorConfig` from the custom module. Your custom client module **must** export all three types — if any is missing, TypeScript will report an unresolvable import error.
175
+ codeBlock:
176
+ lang: typescript
177
+ title: client.ts
178
+ code: |
179
+ export type RequestConfig<TData = unknown> = {
180
+ url?: string
181
+ method: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE'
182
+ params?: object
183
+ data?: TData | FormData
184
+ responseType?: 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream'
185
+ signal?: AbortSignal
186
+ headers?: HeadersInit
187
+ }
188
+
189
+ export type ResponseConfig<TData = unknown> = {
190
+ data: TData
191
+ status: number
192
+ statusText: string
193
+ }
194
+
195
+ export type ResponseErrorConfig<TError = unknown> = TError
196
+
197
+ // The Client type alias is required when using query plugins
198
+ export type Client = <TData, _TError = unknown, TVariables = unknown>(
199
+ config: RequestConfig<TVariables>
200
+ ) => Promise<ResponseConfig<TData>>
201
+
202
+ export const client: Client = async (config) => { /* ... */ }
203
+ export default client
204
+ examples:
205
+ - name: kubb.config.ts
206
+ files:
207
+ - lang: typescript
208
+ code: |
209
+ import { defineConfig } from 'kubb'
210
+ import { pluginClient } from '@kubb/plugin-client'
211
+
212
+ export default defineConfig({
213
+ // ...
214
+ plugins: [
215
+ pluginClient({
216
+ importPath: './src/client.ts', // Path to your custom client
217
+ }),
218
+ ],
219
+ })
220
+ twoslash: false
221
+ tip: |
222
+ Learn more about defining a custom client [here](https://kubb.dev/plugins/plugin-client#importpath).
223
+ - name: dataReturnType
224
+ type: "'data' | 'full'"
225
+ required: false
226
+ default: "'data'"
227
+ description: |
228
+ Return type used when calling the client.
229
+
230
+ - `'data'` returns `ResponseConfig['data']`.
231
+ - `'full'` returns the full `ResponseConfig`.
232
+ examples:
233
+ - name: data
234
+ files:
235
+ - lang: typescript
236
+ code: |
237
+ export async function getPetById<TData>(
238
+ petId: GetPetByIdPathParams,
239
+ ): Promise<ResponseConfig<TData>['data']> {
240
+ ...
241
+ }
242
+ twoslash: false
243
+ - name: full
244
+ files:
245
+ - lang: typescript
246
+ code: |
247
+ export async function getPetById<TData>(
248
+ petId: GetPetByIdPathParams,
249
+ ): Promise<ResponseConfig<TData>> {
250
+ ...
251
+ }
252
+ twoslash: false
253
+ - name: baseURL
254
+ type: string
255
+ required: false
256
+ description: Sets a custom base URL for all generated calls. When not set, the base URL is automatically taken from the OAS spec via the adapter (e.g. the `servers[0].url` field).
257
+ - name: clientType
258
+ type: "'function' | 'class'"
259
+ required: false
260
+ default: "'function'"
261
+ description: Specify whether to use function-based or class-based clients.
262
+ warning: |
263
+ This plugin is only compatible with `clientType: 'function'` (the default). If `clientType: 'class'` is detected, the plugin will automatically generate its own inline function-based client instead of importing from `@kubb/plugin-client`.
264
+ - name: bundle
265
+ type: boolean
266
+ required: false
267
+ default: 'false'
268
+ description: Controls whether the HTTP client runtime is copied into the generated `.kubb` directory.
269
+ body: |
270
+ - `true` adds a `.kubb/fetch.ts` file containing the selected client template (fetch or axios). Generated clients remain self-contained.
271
+ - `false` keeps generated clients slim by importing the shared runtime from `@kubb/plugin-client/clients/{client}`.
272
+ - Override this behavior by providing a custom `client.importPath`.
273
+ - name: paramsType
274
+ type: "'object' | 'inline'"
275
+ required: false
276
+ default: "'inline'"
277
+ description: Defines how parameters are passed to generated functions. Switch between object-style parameters and inline parameters.
278
+ tip: |
279
+ When `paramsType` is set to `'object'`, `pathParams` will also be set to `'object'`.
280
+ body: |
281
+ - `'object'` returns params and pathParams as an object.
282
+ - `'inline'` returns params as comma-separated params.
283
+ examples:
284
+ - name: object
285
+ files:
286
+ - lang: typescript
287
+ code: |
288
+ export async function deletePet(
289
+ {
290
+ petId,
291
+ headers,
292
+ }: {
293
+ petId: DeletePetPathParams['petId']
294
+ headers?: DeletePetHeaderParams
295
+ },
296
+ config: Partial<RequestConfig> = {},
297
+ ) {
298
+ ...
299
+ }
300
+ twoslash: false
301
+ - name: inline
302
+ files:
303
+ - lang: typescript
304
+ code: |
305
+ export async function deletePet(
306
+ petId: DeletePetPathParams['petId'],
307
+ headers?: DeletePetHeaderParams,
308
+ config: Partial<RequestConfig> = {}
309
+ ){
310
+ ...
311
+ }
312
+ twoslash: false
313
+ - name: paramsCasing
314
+ type: "'camelcase'"
315
+ required: false
316
+ description: Transform parameter names to a specific casing format for path, query, and header parameters in generated client code.
317
+ important: |
318
+ When using `paramsCasing`, ensure that `@kubb/plugin-ts` also has the same `paramsCasing` setting. This option automatically maps transformed parameter names back to their original API names in HTTP requests.
319
+ body: |
320
+ - `'camelcase'` transforms parameter names to camelCase.
321
+ examples:
322
+ - name: With paramsCasing camelcase
323
+ files:
324
+ - lang: typescript
325
+ code: |
326
+ // Function parameters use camelCase
327
+ export async function deletePet(
328
+ petId: DeletePetPathParams['petId'], // ✓ camelCase
329
+ headers?: DeletePetHeaderParams,
330
+ config: Partial<RequestConfig> = {}
331
+ ) {
332
+ // Automatically maps back to original name for the API
333
+ const pet_id = petId
334
+
335
+ return fetch({
336
+ method: 'DELETE',
337
+ url: `/pet/${pet_id}`, // Uses original API parameter name
338
+ ...
339
+ })
340
+ }
341
+ twoslash: false
342
+ - name: Without paramsCasing
343
+ files:
344
+ - lang: typescript
345
+ code: |
346
+ // Parameters use original API naming
347
+ export async function deletePet(
348
+ pet_id: DeletePetPathParams['pet_id'], // Original naming
349
+ headers?: DeletePetHeaderParams,
350
+ config: Partial<RequestConfig> = {}
351
+ ) {
352
+ return fetch({
353
+ method: 'DELETE',
354
+ url: `/pet/${pet_id}`,
355
+ ...
356
+ })
357
+ }
358
+ twoslash: false
359
+ tip: |
360
+ The client automatically generates mapping code to convert camelCase parameter names back to the original API format. You write code with developer-friendly camelCase names, but HTTP requests use the exact parameter names from your OpenAPI specification.
361
+ - name: pathParamsType
362
+ type: "'object' | 'inline'"
363
+ required: false
364
+ default: "'inline'"
365
+ description: Defines how pathParams are passed to generated functions.
366
+ body: |
367
+ - `'object'` returns pathParams as an object.
368
+ - `'inline'` returns pathParams as comma-separated params.
369
+ examples:
370
+ - name: object
371
+ files:
372
+ - lang: typescript
373
+ code: |
374
+ export async function getPetById(
375
+ { petId }: GetPetByIdPathParams,
376
+ ) {
377
+ ...
378
+ }
379
+ twoslash: false
380
+ - name: inline
381
+ files:
382
+ - lang: typescript
383
+ code: |
384
+ export async function getPetById(
385
+ petId: GetPetByIdPathParams,
386
+ ) {
387
+ ...
388
+ }
389
+ twoslash: false
390
+ - name: parser
391
+ type: "'client' | 'zod'"
392
+ required: false
393
+ default: "'client'"
394
+ description: Parser used before returning data.
395
+ body: |
396
+ - `'zod'` uses `@kubb/plugin-zod` to parse data.
397
+ - `'client'` returns data without parsing.
398
+ - name: infinite
399
+ type: Infinite | false
400
+ required: false
401
+ default: 'false'
402
+ description: |
403
+ Generate an `infiniteQuery` hook for paginated queries. Pass `false` to disable.
404
+ codeBlock:
405
+ lang: typescript
406
+ title: Infinite
407
+ code: |
408
+ type Infinite = {
409
+ /**
410
+ * Specify the params key used for `pageParam`.
411
+ * @default `'id'`
412
+ */
413
+ queryParam: string
414
+ /**
415
+ * Which field of the data will be used, set it to undefined when no cursor is known.
416
+ * @deprecated Use `nextParam` and `previousParam` instead for more flexible pagination handling.
417
+ */
418
+ cursorParam?: string | undefined
419
+ /**
420
+ * Which field of the data will be used to get the cursor for the next page.
421
+ * Supports dot notation (e.g. 'pagination.next.id') or array path (e.g. ['pagination', 'next', 'id']) to access nested fields.
422
+ */
423
+ nextParam?: string | string[] | undefined
424
+ /**
425
+ * Which field of the data will be used to get the cursor for the previous page.
426
+ * Supports dot notation (e.g. 'pagination.prev.id') or array path (e.g. ['pagination', 'prev', 'id']) to access nested fields.
427
+ */
428
+ previousParam?: string | string[] | undefined
429
+ /**
430
+ * The initial value, the value of the first page.
431
+ * @default `0`
432
+ */
433
+ initialPageParam: unknown
434
+ } | false
435
+ properties:
436
+ - name: queryParam
437
+ type: string
438
+ required: false
439
+ default: "'id'"
440
+ description: Specify the params key used for `pageParam`.
441
+ - name: initialPageParam
442
+ type: unknown
443
+ required: false
444
+ default: '0'
445
+ description: Specify the initial page param value.
446
+ - name: cursorParam
447
+ type: string | undefined
448
+ required: false
449
+ deprecated:
450
+ note: '`cursorParam` is deprecated. Use `nextParam` and `previousParam` instead for more flexible pagination handling.'
451
+ description: Which field of the data will be used, set it to undefined when no cursor is known.
452
+ - name: nextParam
453
+ type: string | string[] | undefined
454
+ required: false
455
+ description: |
456
+ Which field of the data will be used to get the cursor for the next page.
457
+
458
+ Supports dot notation (e.g. `'pagination.next.id'`) or array path (e.g. `['pagination', 'next', 'id']`) to access nested fields.
459
+ - name: previousParam
460
+ type: string | string[] | undefined
461
+ required: false
462
+ description: |
463
+ Which field of the data will be used to get the cursor for the previous page.
464
+
465
+ Supports dot notation (e.g. `'pagination.prev.id'`) or array path (e.g. `['pagination', 'prev', 'id']`) to access nested fields.
466
+ - name: query
467
+ type: Query
468
+ required: false
469
+ description: |
470
+ Override some `useQuery` behaviors.
471
+
472
+ To disable the creation of hooks pass `false`, this will result in only creating `queryOptions`.
473
+ codeBlock:
474
+ lang: typescript
475
+ title: Query
476
+ code: |
477
+ type Query = {
478
+ methods: Array<HttpMethod>
479
+ importPath?: string
480
+ } | false
481
+ properties:
482
+ - name: methods
483
+ type: Array<HttpMethod>
484
+ required: false
485
+ default: "['get']"
486
+ description: Define which HttpMethods can be used for queries.
487
+ - name: importPath
488
+ type: string
489
+ required: false
490
+ default: "'@tanstack/react-query'"
491
+ description: |
492
+ Path to the `useQuery` that will be used to do the `useQuery` functionality.
493
+ It will be used as `import { useQuery } from '${hook.importPath}'`.
494
+ It allows both relative and absolute path. The path will be applied as is, so a relative path should be based on the file being generated.
495
+ - name: queryKey
496
+ type: '(props: { operation: Operation; schemas: OperationSchemas }) => unknown[]'
497
+ required: false
498
+ description: |
499
+ Customize the queryKey that will be used for the query.
500
+
501
+ The function receives an object with:
502
+ - `operation`: The OpenAPI operation object with methods like `getTags()`, `getOperationId()`, etc.
503
+ - `schemas`: An object containing operation schemas including `pathParams`, `queryParams`, `request`, `response`, etc.
504
+ warning: |
505
+ When using a string you need to use `JSON.stringify`.
506
+ details:
507
+ - title: Using tags and path parameters
508
+ body: |-
509
+ Generate a queryKey with operation tags and path parameters:
510
+
511
+ For a GET operation with tags `["user"]` and path parameter `username`, this generates:
512
+ codeBlock:
513
+ - lang: typescript
514
+ code: |-
515
+ import { defineConfig } from 'kubb'
516
+ import { pluginReactQuery } from '@kubb/plugin-react-query'
517
+
518
+ export default defineConfig({
519
+ // ...
520
+ plugins: [
521
+ pluginReactQuery({
522
+ queryKey: ({ operation, schemas }) => {
523
+ const tags = operation.getTags().map(tag => JSON.stringify(tag.name))
524
+ const pathParams = schemas.pathParams?.keys || []
525
+ return [...tags, ...pathParams]
526
+ },
527
+ }),
528
+ ],
529
+ })
530
+ - lang: typescript
531
+ code: |-
532
+ export const getUserByNameQueryKey = ({ username }: { username: GetUserByNamePathParams["username"] }) =>
533
+ ['user', username] as const
534
+ - title: Using the default transformer
535
+ body: |-
536
+ You can extend the default queryKey transformer:
537
+
538
+ This prepends a version to the default queryKey:
539
+ codeBlock:
540
+ - lang: typescript
541
+ code: |-
542
+ import { pluginReactQuery } from '@kubb/plugin-react-query'
543
+ import { QueryKey } from '@kubb/plugin-react-query/components'
544
+
545
+ export default defineConfig({
546
+ // ...
547
+ plugins: [
548
+ pluginReactQuery({
549
+ queryKey: (props) => {
550
+ const defaultKeys = QueryKey.getTransformer(props)
551
+ return [JSON.stringify('v5'), ...defaultKeys]
552
+ },
553
+ }),
554
+ ],
555
+ })
556
+ - lang: typescript
557
+ code: |-
558
+ export const findPetsByTagsQueryKey = (params?: FindPetsByTagsQueryParams) =>
559
+ ['v5', { url: '/pet/findByTags' }, ...(params ? [params] : [])] as const
560
+ - title: Using operation ID
561
+ body: 'Create a simple queryKey using the operation ID:'
562
+ codeBlock:
563
+ lang: typescript
564
+ code: |-
565
+ import { pluginReactQuery } from '@kubb/plugin-react-query'
566
+
567
+ export default defineConfig({
568
+ // ...
569
+ plugins: [
570
+ pluginReactQuery({
571
+ queryKey: ({ operation }) => {
572
+ return [JSON.stringify(operation.getOperationId())]
573
+ },
574
+ }),
575
+ ],
576
+ })
577
+ - title: Conditional keys based on parameters
578
+ body: 'Include query parameters when they exist:'
579
+ codeBlock:
580
+ lang: typescript
581
+ code: |-
582
+ import { pluginReactQuery } from '@kubb/plugin-react-query'
583
+
584
+ export default defineConfig({
585
+ // ...
586
+ plugins: [
587
+ pluginReactQuery({
588
+ queryKey: ({ operation, schemas }) => {
589
+ const keys = [JSON.stringify(operation.getOperationId())]
590
+
591
+ // Add path parameter values (without quotes, so they reference the variables)
592
+ if (schemas.pathParams?.keys) {
593
+ keys.push(...schemas.pathParams.keys)
594
+ }
595
+
596
+ // Add query params conditionally (the string gets embedded as code)
597
+ if (schemas.queryParams?.name) {
598
+ keys.push('...(params ? [params] : [])')
599
+ }
600
+
601
+ return keys
602
+ },
603
+ }),
604
+ ],
605
+ })
606
+ - name: suspense
607
+ type: object | false
608
+ required: false
609
+ description: When set, a `suspenseQuery` hook will be added. This will only work for v5 and react.
610
+ - name: mutation
611
+ type: Mutation
612
+ required: false
613
+ description: |
614
+ Override some `useMutation` behaviors.
615
+
616
+ To disable queries pass `false`.
617
+ codeBlock:
618
+ lang: typescript
619
+ title: Mutation
620
+ code: |
621
+ type Mutation = {
622
+ methods: Array<HttpMethod>
623
+ importPath?: string
624
+ } | false
625
+ properties:
626
+ - name: methods
627
+ type: Array<HttpMethod>
628
+ required: false
629
+ default: "['post', 'put', 'delete']"
630
+ description: Define which HttpMethods can be used for mutations.
631
+ - name: importPath
632
+ type: string
633
+ required: false
634
+ default: "'@tanstack/react-query'"
635
+ description: |
636
+ Path to the `useMutation` that will be used to do the `useMutation` functionality.
637
+ It will be used as `import { useMutation } from '${hook.importPath}'`.
638
+ It allows both relative and absolute path. The path will be applied as is, so a relative path should be based on the file being generated.
639
+ - name: mutationKey
640
+ type: '(props: { operation: Operation; schemas: OperationSchemas }) => unknown[]'
641
+ required: false
642
+ description: Customize the mutationKey.
643
+ warning: |
644
+ When using a string you need to use `JSON.stringify`.
645
+ - name: customOptions
646
+ type: CustomOptions
647
+ required: false
648
+ description: |
649
+ When set, a custom hook will be used to customize the options of the generated hooks.
650
+
651
+ It will also generate a `HookOptions` type that can be used to type the custom options of each hook for type-safety.
652
+ codeBlock:
653
+ lang: typescript
654
+ title: CustomOptions
655
+ code: |
656
+ type CustomOptions = {
657
+ importPath: string
658
+ name?: string
659
+ }
660
+ properties:
661
+ - name: importPath
662
+ type: string
663
+ required: true
664
+ description: |
665
+ Path to the hook that will be used to customize the hook options.
666
+ It will be used as `import ${customOptions.name} from '${customOptions.importPath}'`.
667
+ It allows both relative and absolute paths. However, the path will be applied as is, so relative paths should be based on the file being generated.
668
+ - name: name
669
+ type: string
670
+ required: false
671
+ default: "'useCustomHookOptions'"
672
+ description: |
673
+ Name of the exported hook that will be used to customize the hook options.
674
+ It will be used as `import ${customOptions.name} from '${customOptions.importPath}'`.
675
+ If not provided, it defaults to `useCustomHookOptions`.
676
+ details:
677
+ - title: Add custom hook options to invalidate queries
678
+ codeBlock:
679
+ lang: typescript
680
+ title: useCustomHookOptions.ts
681
+ code: |-
682
+ function getCustomHookOptions({ queryClient }: { queryClient: QueryClient }): Partial<HookOptions> {
683
+ return {
684
+ useUpdatePetHook: {
685
+ onSuccess: () => {
686
+ // Invalidate queries using a constant
687
+ void queryClient.invalidateQueries({ queryKey: ['pet'] })
688
+ },
689
+ },
690
+ useDeletePetHook: {
691
+ onSuccess: (_data, variables, _onMutateResult, _context) => {
692
+ // Invalidate queries using the provided variables
693
+ void queryClient.invalidateQueries({ queryKey: ['pet', variables.pet_id] })
694
+ },
695
+ },
696
+ useUpdateUserHook: {
697
+ onSuccess: (_data, variables, _onMutateResult, _context) => {
698
+ // Invalidate queries using the provided query key generator function
699
+ void queryClient.invalidateQueries({ queryKey: getUserByNameQueryKey({ username: variables.username }) })
700
+ },
701
+ },
702
+ // Add more custom hook options here...
703
+ }
704
+ }
705
+
706
+ export function useCustomHookOptions<T extends keyof HookOptions>({ hookName, operationId }: { hookName: T; operationId: string }): HookOptions[T] {
707
+ const queryClient = useQueryClient()
708
+ const customOptions = getCustomHookOptions({ queryClient })
709
+ return customOptions[hookName] ?? {}
710
+ }
711
+ - title: Add custom hook options to implement custom error handling
712
+ codeBlock:
713
+ lang: typescript
714
+ title: useCustomHookOptions.ts
715
+ code: |-
716
+ function getCustomHookOptions({queryClient}: { queryClient: QueryClient }): Partial<HookOptions> {
717
+ return {
718
+ useUpdatePetHook: {
719
+ onError: (error, _variables, _onMutateResult, _context) => {
720
+ // Log the error
721
+ console.error(`Failed to update pet:`, error);
722
+ },
723
+ },
724
+ useDeletePetHook: {
725
+ onError: (_error, variables, _onMutateResult, _context) => {
726
+ // Show a toast notification
727
+ toast.error(`Failed to delete pet with id '${variables.pet_id}'`);
728
+ },
729
+ },
730
+ useUpdateUserHook: {
731
+ onError: (_error, variables, _onMutateResult, _context) => {
732
+ // Post the error to a custom analytics service
733
+ postEvent('user_updated_failed', { username: variables.username, message: error.message, date: Date.now() })
734
+ },
735
+ },
736
+ // Add more custom hook options here...
737
+ }
738
+ }
739
+
740
+ export function useCustomHookOptions<T extends keyof HookOptions>({ hookName, operationId }: { hookName: T; operationId: string }): HookOptions[T] {
741
+ const queryClient = useQueryClient()
742
+ const customOptions = getCustomHookOptions({queryClient})
743
+ return customOptions[hookName] ?? {}
744
+ }
745
+ - name: include
746
+ type: Array<Include>
747
+ required: false
748
+ description: Array containing include parameters to include tags, operations, methods, paths, or content types.
749
+ codeBlock:
750
+ lang: typescript
751
+ title: Include
752
+ code: |
753
+ export type Include = {
754
+ type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType'
755
+ pattern: string | RegExp
756
+ }
757
+ - name: exclude
758
+ type: Array<Exclude>
759
+ required: false
760
+ description: Array containing exclude parameters to exclude or skip tags, operations, methods, paths, or content types.
761
+ codeBlock:
762
+ lang: typescript
763
+ title: Exclude
764
+ code: |
765
+ export type Exclude = {
766
+ type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType'
767
+ pattern: string | RegExp
768
+ }
769
+ - name: override
770
+ type: Array<Override>
771
+ required: false
772
+ description: Array containing override parameters to override `options` based on tags, operations, methods, paths, or content types.
773
+ codeBlock:
774
+ lang: typescript
775
+ title: Override
776
+ code: |
777
+ export type Override = {
778
+ type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType'
779
+ pattern: string | RegExp
780
+ options: PluginOptions
781
+ }
782
+ - name: generators
783
+ type: Array<Generator<PluginReactQuery>>
784
+ required: false
785
+ experimental: true
786
+ description: |
787
+ Define additional generators next to the built-in generators.
788
+
789
+ See [Generators](https://kubb.dev/docs/5.x/guides/creating-plugins) for more information on how to use generators.
790
+ - name: resolver
791
+ type: Partial<ResolverReactQuery> & ThisType<ResolverReactQuery>
792
+ required: false
793
+ description: |
794
+ A single resolver that overrides the naming and path-resolution conventions. Each method you provide replaces the corresponding built-in one. When a method returns `null` or `undefined`, the resolver's result is used as the fallback, so you only need to supply the methods you want to change.
795
+
796
+ `this` inside each method is bound to the **full** resolver, so you can call other resolver methods (e.g. `this.default(name, 'function')`) without losing context.
797
+ body: |
798
+ Each plugin ships a built-in resolver:
799
+
800
+ | Plugin | Default resolver |
801
+ | --- | --- |
802
+ | `@kubb/plugin-ts` | `resolverTs` |
803
+ | `@kubb/plugin-zod` | `resolverZod` |
804
+ | `@kubb/plugin-cypress` | `resolverCypress` |
805
+ | `@kubb/plugin-mcp` | `resolverMcp` |
806
+ codeBlock:
807
+ lang: typescript
808
+ title: Custom resolver (plugin-ts example)
809
+ code: |
810
+ import { pluginTs } from '@kubb/plugin-ts'
811
+
812
+ pluginTs({
813
+ resolver: {
814
+ resolveName(name) {
815
+ // Prefix every operation-derived name; falls back for names where
816
+ // this returns null/undefined.
817
+ return `Api${this.default(name, 'function')}`
818
+ },
819
+ },
820
+ })
821
+ tip: |
822
+ Use `resolver` for fine-grained control over naming conventions.
823
+ - name: transformer
824
+ type: Visitor
825
+ required: false
826
+ description: |
827
+ A single AST visitor applied to every node before code is printed. Each method you provide replaces the corresponding built-in one. When a method returns `null` or `undefined`, the preset transformer's result is used as the fallback — so you only need to supply the methods you want to change.
828
+
829
+ Visitor methods receive the node and a context object. Return a modified node to replace it, or return `undefined`/`void` to leave it unchanged.
830
+ examples:
831
+ - name: Strip descriptions before printing
832
+ files:
833
+ - lang: typescript
834
+ code: |
835
+ import { pluginTs } from '@kubb/plugin-ts'
836
+
837
+ pluginTs({
838
+ transformer: {
839
+ schema(node) {
840
+ return { ...node, description: undefined }
841
+ },
842
+ },
843
+ })
844
+ twoslash: false
845
+ - name: Prefix every operationId
846
+ files:
847
+ - lang: typescript
848
+ code: |
849
+ import { pluginTs } from '@kubb/plugin-ts'
850
+
851
+ pluginTs({
852
+ transformer: {
853
+ operation(node) {
854
+ return { ...node, operationId: `api_${node.operationId}` }
855
+ },
856
+ },
857
+ })
858
+ twoslash: false
859
+ tip: |
860
+ Use `transformer` to rewrite node properties before printing. For output naming customization, use `resolver` instead.
861
+ notes:
862
+ - type: tip
863
+ body: |
864
+ See [Tanstack Query](https://tanstack.com/query) for more information about React Query.
865
+ examples:
866
+ - name: kubb.config.ts
867
+ files:
868
+ - lang: typescript
869
+ code: |
870
+ import { defineConfig } from 'kubb'
871
+ import { pluginTs } from '@kubb/plugin-ts'
872
+ import { pluginReactQuery } from '@kubb/plugin-react-query'
873
+
874
+ export default defineConfig({
875
+ input: {
876
+ path: './petStore.yaml',
877
+ },
878
+ output: {
879
+ path: './src/gen',
880
+ },
881
+ plugins: [
882
+ pluginTs(),
883
+ pluginReactQuery({
884
+ output: {
885
+ path: './hooks',
886
+ },
887
+ group: {
888
+ type: 'tag',
889
+ name: ({ group }) => `${group}Hooks`,
890
+ },
891
+ client: {
892
+ dataReturnType: 'full',
893
+ },
894
+ mutation: {
895
+ methods: ['post', 'put', 'delete'],
896
+ },
897
+ infinite: {
898
+ queryParam: 'next_page',
899
+ initialPageParam: 0,
900
+ nextParam: 'pagination.next.cursor',
901
+ previousParam: ['pagination', 'prev', 'cursor'],
902
+ },
903
+ query: {
904
+ methods: ['get'],
905
+ importPath: '@tanstack/react-query',
906
+ },
907
+ suspense: {},
908
+ }),
909
+ ],
910
+ })
911
+ twoslash: false