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

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