@kubb/plugin-react-query 5.0.0-beta.4 → 5.0.0-beta.42

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 (49) hide show
  1. package/README.md +38 -91
  2. package/dist/{components-dAKJEn9b.cjs → components-DQAYLQW0.cjs} +409 -279
  3. package/dist/components-DQAYLQW0.cjs.map +1 -0
  4. package/dist/{components-DTGLu4UV.js → components-IArDg-DO.js} +379 -279
  5. package/dist/components-IArDg-DO.js.map +1 -0
  6. package/dist/components.cjs +1 -1
  7. package/dist/components.d.ts +5 -77
  8. package/dist/components.js +1 -1
  9. package/dist/{generators-C_fbcjpG.js → generators-B86BJkmW.js} +346 -419
  10. package/dist/generators-B86BJkmW.js.map +1 -0
  11. package/dist/{generators-CWEQsdO9.cjs → generators-BqGaMUH6.cjs} +344 -417
  12. package/dist/generators-BqGaMUH6.cjs.map +1 -0
  13. package/dist/generators.cjs +1 -1
  14. package/dist/generators.d.ts +49 -10
  15. package/dist/generators.js +1 -1
  16. package/dist/index.cjs +176 -26
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.ts +32 -4
  19. package/dist/index.js +177 -27
  20. package/dist/index.js.map +1 -1
  21. package/dist/types-Dh4HNR9K.d.ts +400 -0
  22. package/extension.yaml +910 -364
  23. package/package.json +15 -17
  24. package/src/components/InfiniteQuery.tsx +24 -13
  25. package/src/components/InfiniteQueryOptions.tsx +37 -55
  26. package/src/components/Mutation.tsx +35 -15
  27. package/src/components/MutationOptions.tsx +14 -13
  28. package/src/components/Query.tsx +14 -10
  29. package/src/components/QueryOptions.tsx +17 -34
  30. package/src/components/SuspenseInfiniteQuery.tsx +19 -13
  31. package/src/components/SuspenseInfiniteQueryOptions.tsx +28 -52
  32. package/src/components/SuspenseQuery.tsx +9 -10
  33. package/src/generators/customHookOptionsFileGenerator.tsx +18 -14
  34. package/src/generators/hookOptionsGenerator.tsx +44 -51
  35. package/src/generators/infiniteQueryGenerator.tsx +57 -78
  36. package/src/generators/mutationGenerator.tsx +53 -64
  37. package/src/generators/queryGenerator.tsx +54 -63
  38. package/src/generators/suspenseInfiniteQueryGenerator.tsx +52 -65
  39. package/src/generators/suspenseQueryGenerator.tsx +56 -76
  40. package/src/plugin.ts +45 -31
  41. package/src/resolvers/resolverReactQuery.ts +102 -6
  42. package/src/types.ts +199 -61
  43. package/src/utils.ts +10 -33
  44. package/dist/components-DTGLu4UV.js.map +0 -1
  45. package/dist/components-dAKJEn9b.cjs.map +0 -1
  46. package/dist/generators-CWEQsdO9.cjs.map +0 -1
  47. package/dist/generators-C_fbcjpG.js.map +0 -1
  48. package/dist/types-DfaFRSBf.d.ts +0 -284
  49. /package/dist/{chunk--u3MIqq1.js → chunk-C0LytTxp.js} +0 -0
package/extension.yaml CHANGED
@@ -2,18 +2,18 @@ $schema: https://kubb.dev/schemas/extension.json
2
2
  kind: plugin
3
3
  id: plugin-react-query
4
4
  name: React Query
5
- description: Generate React Query hooks (useQuery, useMutation) from OpenAPI specifications.
5
+ description: Generate TanStack Query hooks for React (useQuery, useMutation, useSuspenseQuery, useInfiniteQuery) from OpenAPI.
6
6
  category: framework
7
7
  type: official
8
- npmPackage: "@kubb/plugin-react-query"
8
+ npmPackage: '@kubb/plugin-react-query'
9
9
  docsPath: /plugins/plugin-react-query
10
10
  repo: https://github.com/kubb-labs/plugins
11
11
  maintainers:
12
12
  - name: Stijn Van Hulle
13
13
  github: stijnvanhulle
14
14
  compatibility:
15
- kubb: ">=5.0.0"
16
- node: ">=22"
15
+ kubb: '>=5.0.0'
16
+ node: '>=22'
17
17
  tags:
18
18
  - react-query
19
19
  - tanstack-query
@@ -37,159 +37,399 @@ icon:
37
37
  intro: |
38
38
  # @kubb/plugin-react-query
39
39
 
40
- Generate type-safe React Query hooks from your OpenAPI schema for data fetching, caching, and synchronization.
40
+ Generate one [TanStack Query](https://tanstack.com/query) hook per OpenAPI operation. Queries become `useFooQuery`/`useFooSuspenseQuery`/`useFooInfiniteQuery`; mutations become `useFooMutation`. Each hook is fully typed: query keys, input variables, response data, and error shape all come from the spec.
41
+
42
+ Pairs with `@kubb/plugin-client` for the HTTP layer and `@kubb/plugin-ts` for types.
41
43
  options:
42
44
  - name: output
43
45
  type: Output
44
46
  required: false
45
- default: "{ path: 'hooks', barrelType: 'named' }"
46
- description: Specify the export location for the files and define the behavior of the output.
47
+ default: "{ path: 'hooks', barrel: { type: 'named' } }"
48
+ description: Where the generated hooks are written and how they are exported.
47
49
  properties:
48
50
  - name: path
49
51
  type: string
50
52
  required: true
51
- description: Output directory or file for the generated code, relative to the global `output.path`.
53
+ description: |
54
+ Folder (or single file) where the plugin writes its generated code. The path is resolved against the global `output.path` set on `defineConfig`.
55
+
56
+ Use a folder to keep each generator's output isolated (`'types'`, `'clients'`, `'hooks'`). Use a single file when you want everything in one place, for example `'api.ts'`.
52
57
  tip: |
53
- if `output.path` is a file, `group` cannot be used.
58
+ When `output.path` points to a single file, the `group` option cannot be used because every operation ends up in the same file.
59
+ examples:
60
+ - name: kubb.config.ts
61
+ files:
62
+ - lang: typescript
63
+ twoslash: false
64
+ code: |
65
+ import { defineConfig } from 'kubb'
66
+ import { pluginTs } from '@kubb/plugin-ts'
67
+
68
+ export default defineConfig({
69
+ input: { path: './petStore.yaml' },
70
+ output: { path: './src/gen' },
71
+ plugins: [
72
+ pluginTs({
73
+ output: { path: './types' },
74
+ }),
75
+ ],
76
+ })
77
+ - name: Resulting tree
78
+ files:
79
+ - lang: text
80
+ twoslash: false
81
+ code: |
82
+ src/
83
+ └── gen/
84
+ └── types/
85
+ ├── Pet.ts
86
+ └── Store.ts
54
87
  default: "'hooks'"
55
- - name: barrelType
56
- type: "'all' | 'named' | 'propagate' | false"
88
+ - name: barrel
89
+ type: "{ type: 'named' | 'all', nested?: boolean } | false"
57
90
  required: false
58
- default: "'named'"
59
- description: Specify what to export and optionally disable barrel-file generation.
91
+ default: "{ type: 'named' }"
92
+ description: |
93
+ Controls how the generated `index.ts` (barrel) file re-exports the plugin's output.
94
+
95
+ - `{ type: 'named' }` re-exports each symbol by name. Best for tree-shaking and explicit imports.
96
+ - `{ type: 'all' }` uses `export *`. Smaller barrel file, but exports everything.
97
+ - `{ nested: true }` creates a barrel in every subdirectory, so callers can import from any depth.
98
+ - `false` skips the barrel entirely. The plugin's files are also excluded from the root `index.ts`.
60
99
  tip: |
61
- Using `propagate` will prevent a plugin from creating a barrel file, but it will still propagate, allowing [`output.barrelType`](https://kubb.dev/docs/5.x/configuration#output-barreltype) to export the specific function or type.
100
+ Pick `'named'` when consumers care about which symbols they import (better tree-shaking, friendlier auto-import). Pick `'all'` when the file count is small and you want a one-line barrel.
62
101
  examples:
63
- - name: all
102
+ - name: "'named' (default)"
64
103
  files:
65
- - lang: typescript
104
+ - name: kubb.config.ts
105
+ lang: typescript
106
+ twoslash: false
66
107
  code: |
67
- export * from './gen/petService.ts'
108
+ import { defineConfig } from 'kubb'
109
+ import { pluginTs } from '@kubb/plugin-ts'
110
+
111
+ export default defineConfig({
112
+ input: { path: './petStore.yaml' },
113
+ output: { path: './src/gen' },
114
+ plugins: [
115
+ pluginTs({
116
+ output: { barrel: { type: 'named' } },
117
+ }),
118
+ ],
119
+ })
120
+ - name: src/gen/types/index.ts
121
+ lang: typescript
68
122
  twoslash: false
69
- - name: named
123
+ code: |
124
+ export { Pet, PetStatus } from './Pet'
125
+ export { Store } from './Store'
126
+ - name: "'all'"
70
127
  files:
71
- - lang: typescript
128
+ - name: kubb.config.ts
129
+ lang: typescript
130
+ twoslash: false
72
131
  code: |
73
- export { PetService } from './gen/petService.ts'
132
+ import { defineConfig } from 'kubb'
133
+ import { pluginTs } from '@kubb/plugin-ts'
134
+
135
+ export default defineConfig({
136
+ input: { path: './petStore.yaml' },
137
+ output: { path: './src/gen' },
138
+ plugins: [
139
+ pluginTs({
140
+ output: { barrel: { type: 'all' } },
141
+ }),
142
+ ],
143
+ })
144
+ - name: src/gen/types/index.ts
145
+ lang: typescript
74
146
  twoslash: false
75
- - name: propagate
147
+ code: |
148
+ export * from './Pet'
149
+ export * from './Store'
150
+ - name: nested
76
151
  files:
77
- - lang: typescript
78
- code: ""
152
+ - name: kubb.config.ts
153
+ lang: typescript
79
154
  twoslash: false
80
- - name: "false"
155
+ code: |
156
+ import { defineConfig } from 'kubb'
157
+ import { pluginTs } from '@kubb/plugin-ts'
158
+
159
+ export default defineConfig({
160
+ input: { path: './petStore.yaml' },
161
+ output: { path: './src/gen' },
162
+ plugins: [
163
+ pluginTs({
164
+ output: { barrel: { type: 'named', nested: true } },
165
+ }),
166
+ ],
167
+ })
168
+ - name: Generated tree
169
+ lang: text
170
+ twoslash: false
171
+ code: |
172
+ src/gen/types/
173
+ ├── index.ts # re-exports ./petController and ./storeController
174
+ ├── petController/
175
+ │ ├── index.ts # re-exports Pet, Store, ...
176
+ │ └── Pet.ts
177
+ └── storeController/
178
+ ├── index.ts
179
+ └── Store.ts
180
+ - name: 'false'
81
181
  files:
82
- - lang: typescript
83
- code: ""
182
+ - name: kubb.config.ts
183
+ lang: typescript
84
184
  twoslash: false
185
+ code: |
186
+ import { defineConfig } from 'kubb'
187
+ import { pluginTs } from '@kubb/plugin-ts'
188
+
189
+ export default defineConfig({
190
+ input: { path: './petStore.yaml' },
191
+ output: { path: './src/gen' },
192
+ plugins: [
193
+ pluginTs({
194
+ output: { barrel: false },
195
+ }),
196
+ ],
197
+ })
198
+ - name: Result
199
+ lang: text
200
+ twoslash: false
201
+ code: |
202
+ # No index.ts is generated for this plugin.
203
+ # Its files are also excluded from the root index.ts.
85
204
  - name: banner
86
- type: "string | ((node: RootNode) => string)"
205
+ type: 'string | ((node: RootNode) => string)'
87
206
  required: false
88
- 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.
207
+ description: |
208
+ Text prepended to every generated file. Useful for license headers, lint disables, or `@ts-nocheck` directives.
209
+
210
+ Pass a string for a static banner. Pass a function to compute the banner from each file's `RootNode` (the AST root containing path, schema, and operation context).
211
+ examples:
212
+ - name: Static banner
213
+ files:
214
+ - name: kubb.config.ts
215
+ lang: typescript
216
+ twoslash: false
217
+ code: |
218
+ import { defineConfig } from 'kubb'
219
+ import { pluginTs } from '@kubb/plugin-ts'
220
+
221
+ export default defineConfig({
222
+ input: { path: './petStore.yaml' },
223
+ output: { path: './src/gen' },
224
+ plugins: [
225
+ pluginTs({
226
+ output: {
227
+ banner: '/* eslint-disable */\n// @ts-nocheck',
228
+ },
229
+ }),
230
+ ],
231
+ })
232
+ - name: Generated file
233
+ lang: typescript
234
+ twoslash: false
235
+ code: |
236
+ /* eslint-disable */
237
+ // @ts-nocheck
238
+ export type Pet = {
239
+ id: number
240
+ name: string
241
+ }
242
+ - name: Dynamic banner
243
+ files:
244
+ - name: kubb.config.ts
245
+ lang: typescript
246
+ twoslash: false
247
+ code: |
248
+ import { defineConfig } from 'kubb'
249
+ import { pluginTs } from '@kubb/plugin-ts'
250
+
251
+ export default defineConfig({
252
+ input: { path: './petStore.yaml' },
253
+ output: { path: './src/gen' },
254
+ plugins: [
255
+ pluginTs({
256
+ output: {
257
+ banner: (node) => `// Source: ${node.path}\n// Generated at ${new Date().toISOString()}`,
258
+ },
259
+ }),
260
+ ],
261
+ })
89
262
  - name: footer
90
- type: "string | ((node: RootNode) => string)"
263
+ type: 'string | ((node: RootNode) => string)'
91
264
  required: false
92
- 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.
265
+ description: |
266
+ Text appended at the end of every generated file. The mirror of `banner` — use it for closing comments, re-enabling lint rules, or marker lines.
267
+
268
+ Pass a string for a static footer, or a function that receives the file's `RootNode` and returns the footer text.
269
+ examples:
270
+ - name: Re-enable lint after a banner disable
271
+ files:
272
+ - name: kubb.config.ts
273
+ lang: typescript
274
+ twoslash: false
275
+ code: |
276
+ import { defineConfig } from 'kubb'
277
+ import { pluginTs } from '@kubb/plugin-ts'
278
+
279
+ export default defineConfig({
280
+ input: { path: './petStore.yaml' },
281
+ output: { path: './src/gen' },
282
+ plugins: [
283
+ pluginTs({
284
+ output: {
285
+ banner: '/* eslint-disable */',
286
+ footer: '/* eslint-enable */',
287
+ },
288
+ }),
289
+ ],
290
+ })
93
291
  - name: override
94
292
  type: boolean
95
293
  required: false
96
- default: "false"
97
- description: Whether Kubb overrides existing external files that can be generated if they already exist.
98
- - name: contentType
99
- type: "'application/json' | (string & {})"
100
- required: false
101
- description: |
102
- Define which content type to use.
294
+ default: 'false'
295
+ description: |
296
+ Allows the plugin to overwrite hand-written files that share a name with a generated file.
103
297
 
104
- By default, Kubb uses the first JSON-valid media type.
298
+ - `false` (default): Kubb skips a file if it already exists and is not marked as generated. This protects manual edits.
299
+ - `true`: Kubb overwrites any file at the target path, including hand-written ones.
300
+ warning: |
301
+ Enable this only when you are sure the target folder contains nothing you need to keep. Local edits are lost on the next generation.
302
+ examples:
303
+ - name: kubb.config.ts
304
+ files:
305
+ - lang: typescript
306
+ twoslash: false
307
+ code: |
308
+ import { defineConfig } from 'kubb'
309
+ import { pluginTs } from '@kubb/plugin-ts'
310
+
311
+ export default defineConfig({
312
+ input: { path: './petStore.yaml' },
313
+ output: { path: './src/gen' },
314
+ plugins: [
315
+ pluginTs({
316
+ output: { override: true },
317
+ }),
318
+ ],
319
+ })
105
320
  - name: group
106
321
  type: Group
107
322
  required: false
108
323
  description: |
109
- Grouping combines files in a folder based on a specific `type`.
324
+ Splits generated files into subfolders based on the operation's tag, so each tag in your OpenAPI spec gets its own directory.
325
+
326
+ Without `group`, every file lands in the plugin's `output.path` folder. With `group`, files are bucketed under `{output.path}/{groupName}/`, where `groupName` is derived from the operation's first tag.
327
+ tip: |
328
+ Use `group` to mirror your API's domain structure (pet, store, user) in the generated code. Combine it with `output.barrel: { type: 'named', nested: true }` to get per-tag barrel files.
110
329
  examples:
111
330
  - name: kubb.config.ts
112
331
  files:
113
332
  - lang: typescript
114
- code: |
115
- group: {
116
- type: 'tag',
117
- name({ group }) {
118
- return `${group}Controller`
119
- }
120
- }
121
333
  twoslash: false
334
+ code: |
335
+ import { defineConfig } from 'kubb'
336
+ import { pluginTs } from '@kubb/plugin-ts'
337
+
338
+ export default defineConfig({
339
+ input: { path: './petStore.yaml' },
340
+ output: { path: './src/gen' },
341
+ plugins: [
342
+ pluginTs({
343
+ group: {
344
+ type: 'tag',
345
+ name: ({ group }) => `${group}Controller`,
346
+ },
347
+ }),
348
+ ],
349
+ })
122
350
  body: |
123
351
  With the configuration above, the generator emits:
124
352
 
125
353
  ```text
126
- .
127
- ├── src/
128
- └── petController/
129
- │ ├── addPet.ts
130
- │ │ └── getPet.ts
131
- │ └── storeController/
132
- │ ├── createStore.ts
133
- │ └── getStoreById.ts
134
- ├── petStore.yaml
135
- ├── kubb.config.ts
136
- └── package.json
354
+ src/gen/
355
+ ├── petController/
356
+ ├── AddPet.ts
357
+ └── GetPet.ts
358
+ └── storeController/
359
+ ├── CreateStore.ts
360
+ └── GetStoreById.ts
137
361
  ```
138
362
  properties:
139
363
  - name: type
140
364
  type: "'tag'"
141
365
  required: true
142
- description: Specify the property to group files by. Required when `group` is defined.
366
+ description: |
367
+ Property used to assign each operation to a group. Required whenever `group` is set.
368
+
369
+ Today only `'tag'` is supported: Kubb reads the first tag on the operation (`operation.getTags().at(0)?.name`) and uses it as the group key. Operations without a tag are placed in a default group.
143
370
  note: |
144
- `Required: true*` means this is required only when the `group` option is used. The `group` option itself is optional.
145
- body: |
146
- - `'tag'`: Uses the first tag from `operation.getTags().at(0)?.name`
371
+ `Required: true*` is conditional only required when the parent `group` option is used. `group` itself stays optional.
147
372
  - name: name
148
- type: "(context: GroupContext) => string"
373
+ type: '(context: GroupContext) => string'
149
374
  required: false
150
375
  default: (ctx) => `${ctx.group}Controller`
151
- description: Return the name of a group based on the group name. This is used for file and identifier generation.
376
+ description: Function that builds the folder/identifier name from a group key (the operation's first tag).
152
377
  - name: client
153
378
  type: ClientImportPath & { clientType?, dataReturnType?, baseURL?, bundle? }
154
379
  required: false
155
- description: Client configuration for HTTP request generation.
380
+ description: |
381
+ HTTP client used inside every generated hook. Each generated hook calls into this client to perform the actual request.
382
+
383
+ Mirrors a subset of `pluginClient` options. Set these here when the React Query hooks need different client behavior than the rest of your app (for example, a different base URL or full response objects).
156
384
  properties:
157
385
  - name: importPath
158
386
  type: string
159
387
  required: false
160
- description: Path to the client used for API calls. Supports both relative and absolute paths.
388
+ description: |
389
+ Path or module specifier of a custom client module. Generated code imports its HTTP runtime from here instead of `@kubb/plugin-client/clients/{client}`.
390
+
391
+ Use this when you need to inject auth headers, add interceptors, change the base URL at runtime, or wrap a different HTTP library (ky, ofetch, ...). Both relative paths (`./src/client.ts`) and bare specifiers (`@my-org/api-client`) work.
161
392
  details:
162
393
  - title: When to use `importPath`
163
394
  body: |
164
- Use `importPath` when you want to:
395
+ Reach for a custom client when you need to:
165
396
 
166
- - **Customize the HTTP client**: Provide your own client implementation with custom configurations (e.g., baseURL, headers, interceptors)
167
- - **Add authentication**: Include authentication tokens or other security mechanisms in your client
168
- - **Override default behavior**: Replace the default Kubb client with your own implementation
397
+ - Add an auth token to every request.
398
+ - Plug in interceptors, retries, or logging.
399
+ - Configure `baseURL` and headers from environment variables.
400
+ - Wrap a library other than `axios`/`fetch`.
169
401
  - title: Default behavior
170
402
  body: |
171
- When `importPath` is not specified:
403
+ Without `importPath`:
172
404
 
173
- - If `bundle: false` (default): Uses `@kubb/plugin-client/clients/${client}` where client is either `axios` or `fetch`.
174
- - If `bundle: true`: Bundles the client into `.kubb/fetch.ts`.
175
- - title: Import structure
176
- body: "Generated code imports the client as a default import and the runtime types as named type imports:"
405
+ - `bundle: false` (default) generated code imports from `@kubb/plugin-client/clients/{axios|fetch}`.
406
+ - `bundle: true` Kubb writes `.kubb/client.ts` and generated code imports from there.
407
+ - title: Required exports
408
+ body: |
409
+ The module pointed to by `importPath` must satisfy the same shape as the built-in client. At minimum:
410
+
411
+ - A default export of the `client` function.
412
+ - A `RequestConfig` type.
413
+ - A `ResponseErrorConfig` type.
414
+
415
+ When used together with a query plugin (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`), it must also export a `Client` type alias.
416
+ - title: How generated files import it
417
+ body: |
418
+ Generated code imports the client as a default import (bound to the local name `client`) and the runtime types as named type imports:
177
419
  codeBlock:
178
420
  lang: typescript
179
- code: |-
180
- /**
181
- * Generated by Kubb (https://kubb.dev/).
182
- * Do not edit manually.
183
- */
421
+ code: |
184
422
  import client from '${client.importPath}'
185
423
  import type { RequestConfig, ResponseErrorConfig } from '${client.importPath}'
186
- // ... rest of generated file
424
+ // ... rest of the generated file
187
425
  important: |
188
- 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 typesif any is missing, TypeScript will report an unresolvable import error.
426
+ When used with query plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`), generated hooks also import a `Client` type alias. Your module **must** export `Client`, `RequestConfig`, and `ResponseErrorConfig` — TypeScript will fail the import otherwise.
189
427
  codeBlock:
190
428
  lang: typescript
191
- title: client.ts
429
+ title: src/client.ts
192
430
  code: |
431
+ import axios from 'axios'
432
+
193
433
  export type RequestConfig<TData = unknown> = {
194
434
  url?: string
195
435
  method: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE'
@@ -208,96 +448,191 @@ options:
208
448
 
209
449
  export type ResponseErrorConfig<TError = unknown> = TError
210
450
 
211
- // The Client type alias is required when using query plugins
451
+ // Required when used with @kubb/plugin-react-query or @kubb/plugin-vue-query
212
452
  export type Client = <TData, _TError = unknown, TVariables = unknown>(
213
- config: RequestConfig<TVariables>
453
+ config: RequestConfig<TVariables>,
214
454
  ) => Promise<ResponseConfig<TData>>
215
455
 
216
- export const client: Client = async (config) => { /* ... */ }
456
+ const client: Client = async (config) => {
457
+ const response = await axios.request<TData>({
458
+ ...config,
459
+ headers: {
460
+ Authorization: `Bearer ${process.env.API_TOKEN}`,
461
+ ...config.headers,
462
+ },
463
+ })
464
+
465
+ return response
466
+ }
467
+
217
468
  export default client
218
469
  examples:
219
- - name: kubb.config.ts
470
+ - name: Wire up a custom client
220
471
  files:
221
- - lang: typescript
472
+ - name: kubb.config.ts
473
+ lang: typescript
474
+ twoslash: false
222
475
  code: |
223
476
  import { defineConfig } from 'kubb'
224
477
  import { pluginClient } from '@kubb/plugin-client'
225
478
 
226
479
  export default defineConfig({
227
- // ...
480
+ input: { path: './petStore.yaml' },
481
+ output: { path: './src/gen' },
228
482
  plugins: [
229
483
  pluginClient({
230
- importPath: './src/client.ts', // Path to your custom client
484
+ importPath: './src/client.ts',
231
485
  }),
232
486
  ],
233
487
  })
234
- twoslash: false
235
488
  tip: |
236
- Learn more about defining a custom client [here](https://kubb.dev/plugins/plugin-client#importpath).
489
+ See the [custom client guide](https://kubb.dev/plugins/plugin-client#importpath) for a worked example.
237
490
  - name: dataReturnType
238
491
  type: "'data' | 'full'"
239
492
  required: false
240
493
  default: "'data'"
241
494
  description: |
242
- Return type used when calling the client.
495
+ Shape of the value returned from each generated client function.
243
496
 
244
- - `'data'` returns `ResponseConfig['data']`.
245
- - `'full'` returns the full `ResponseConfig`.
497
+ - `'data'` returns only the response body (`response.data`). Concise and matches what most apps need.
498
+ - `'full'` returns the full response config — body, status code, headers, and the original request. Use this when callers need to inspect headers or status.
246
499
  examples:
247
- - name: data
500
+ - name: "'data' (default)"
248
501
  files:
249
- - lang: typescript
502
+ - name: getPetById.ts
503
+ lang: typescript
504
+ twoslash: false
250
505
  code: |
251
506
  export async function getPetById<TData>(
252
507
  petId: GetPetByIdPathParams,
253
508
  ): Promise<ResponseConfig<TData>['data']> {
254
- ...
509
+ // ...
255
510
  }
511
+ - name: usage.ts
512
+ lang: typescript
256
513
  twoslash: false
257
- - name: full
514
+ code: |
515
+ const pet = await getPetById(1)
516
+ // ^? Pet
517
+ - name: "'full'"
258
518
  files:
259
- - lang: typescript
519
+ - name: getPetById.ts
520
+ lang: typescript
521
+ twoslash: false
260
522
  code: |
261
523
  export async function getPetById<TData>(
262
524
  petId: GetPetByIdPathParams,
263
525
  ): Promise<ResponseConfig<TData>> {
264
- ...
526
+ // ...
265
527
  }
528
+ - name: usage.ts
529
+ lang: typescript
266
530
  twoslash: false
531
+ code: |
532
+ const response = await getPetById(1)
533
+ // ^? ResponseConfig<Pet>
534
+ console.log(response.status, response.headers)
535
+ const pet = response.data
267
536
  - name: baseURL
268
537
  type: string
269
538
  required: false
270
- 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).
539
+ description: |
540
+ Base URL prepended to every request URL in the generated client. When omitted, the URL comes from the OpenAPI spec's `servers[0].url` (or whichever index the adapter is configured to read).
541
+
542
+ Set this when the generated client should point at a different environment (staging, production) than the one written in the spec.
543
+ examples:
544
+ - name: Override the spec's server URL
545
+ files:
546
+ - lang: typescript
547
+ twoslash: false
548
+ code: |
549
+ import { defineConfig } from 'kubb'
550
+ import { pluginClient } from '@kubb/plugin-client'
551
+
552
+ export default defineConfig({
553
+ input: { path: './petStore.yaml' },
554
+ output: { path: './src/gen' },
555
+ plugins: [
556
+ pluginClient({
557
+ baseURL: 'https://petstore.swagger.io/v2',
558
+ }),
559
+ ],
560
+ })
271
561
  - name: clientType
272
562
  type: "'function' | 'class'"
273
563
  required: false
274
564
  default: "'function'"
275
- description: Specify whether to use function-based or class-based clients.
565
+ description: |
566
+ Style of the HTTP client that this plugin imports from `@kubb/plugin-client`.
567
+
568
+ - `'function'` — imports the function client (`getPetById(...)`). Required for query plugins.
569
+ - `'class'` — also generates a wrapper class on top, but only usable inside `@kubb/plugin-client`.
276
570
  warning: |
277
- 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`.
571
+ Query plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-svelte-query`, `@kubb/plugin-solid-query`) work only with `clientType: 'function'`. If you set `clientType: 'class'` here, the plugin falls back to generating its own inline function-based client instead of importing from `@kubb/plugin-client`.
278
572
  - name: bundle
279
573
  type: boolean
280
574
  required: false
281
- default: "false"
282
- description: Controls whether the HTTP client runtime is copied into the generated `.kubb` directory.
283
- body: |
284
- - `true` adds a `.kubb/fetch.ts` file containing the selected client template (fetch or axios). Generated clients remain self-contained.
285
- - `false` keeps generated clients slim by importing the shared runtime from `@kubb/plugin-client/clients/{client}`.
286
- - Override this behavior by providing a custom `client.importPath`.
575
+ default: 'false'
576
+ description: |
577
+ Copies the HTTP client runtime into the generated output, so the consuming app does not need `@kubb/plugin-client` installed at runtime.
578
+
579
+ - `false` (default) generated files import from `@kubb/plugin-client/clients/{client}`. Smaller diff, but the package must be a runtime dependency.
580
+ - `true` Kubb writes a `.kubb/client.ts` file with the client implementation. Generated code imports from that local file and the project no longer pulls `@kubb/plugin-client` at runtime.
581
+ - Setting `client.importPath` overrides both behaviors and uses your custom client instead.
582
+ examples:
583
+ - name: Bundle the runtime
584
+ files:
585
+ - lang: typescript
586
+ twoslash: false
587
+ code: |
588
+ import { defineConfig } from 'kubb'
589
+ import { pluginClient } from '@kubb/plugin-client'
590
+
591
+ export default defineConfig({
592
+ input: { path: './petStore.yaml' },
593
+ output: { path: './src/gen' },
594
+ plugins: [
595
+ pluginClient({
596
+ client: 'fetch',
597
+ bundle: true,
598
+ }),
599
+ ],
600
+ })
287
601
  - name: paramsType
288
602
  type: "'object' | 'inline'"
289
603
  required: false
290
604
  default: "'inline'"
291
- description: Defines how parameters are passed to generated functions. Switch between object-style parameters and inline parameters.
605
+ description: |
606
+ How operation parameters (path, query, headers) are exposed in the generated function signature.
607
+
608
+ - `'inline'` (default) — each parameter is a separate positional argument. Compact for operations with one or two params.
609
+ - `'object'` — every parameter is wrapped in a single object argument. Easier to read for operations with many params and named at the call site.
292
610
  tip: |
293
- When `paramsType` is set to `'object'`, `pathParams` will also be set to `'object'`.
294
- body: |
295
- - `'object'` returns params and pathParams as an object.
296
- - `'inline'` returns params as comma-separated params.
611
+ Setting `paramsType: 'object'` implicitly sets `pathParamsType: 'object'` as well, so call sites are consistent.
297
612
  examples:
298
- - name: object
613
+ - name: "'inline' (default)"
299
614
  files:
300
- - lang: typescript
615
+ - name: Generated client
616
+ lang: typescript
617
+ twoslash: false
618
+ code: |
619
+ export async function deletePet(
620
+ petId: DeletePetPathParams['petId'],
621
+ headers?: DeletePetHeaderParams,
622
+ config: Partial<RequestConfig> = {},
623
+ ) {
624
+ // ...
625
+ }
626
+ - name: Caller
627
+ lang: typescript
628
+ twoslash: false
629
+ code: |
630
+ await deletePet(42)
631
+ - name: "'object'"
632
+ files:
633
+ - name: Generated client
634
+ lang: typescript
635
+ twoslash: false
301
636
  code: |
302
637
  export async function deletePet(
303
638
  {
@@ -309,141 +644,152 @@ options:
309
644
  },
310
645
  config: Partial<RequestConfig> = {},
311
646
  ) {
312
- ...
647
+ // ...
313
648
  }
649
+ - name: Caller
650
+ lang: typescript
314
651
  twoslash: false
315
- - name: inline
316
- files:
317
- - lang: typescript
318
652
  code: |
319
- export async function deletePet(
320
- petId: DeletePetPathParams['petId'],
321
- headers?: DeletePetHeaderParams,
322
- config: Partial<RequestConfig> = {}
323
- ){
324
- ...
325
- }
326
- twoslash: false
653
+ await deletePet({ petId: 42, headers: { 'X-Api-Key': 'secret' } })
327
654
  - name: paramsCasing
328
655
  type: "'camelcase'"
329
656
  required: false
330
- description: Transform parameter names to a specific casing format for path, query, and header parameters in generated client code.
657
+ description: |
658
+ Renames path, query, and header parameters in the generated client to the chosen casing. The HTTP request still uses the original names from the OpenAPI spec — Kubb writes the mapping for you.
659
+
660
+ - `'camelcase'` — turn `pet_id` and `X-Api-Key` into `petId` and `xApiKey` in your TypeScript code. The runtime URL still uses `/pet/{pet_id}` and the header is still sent as `X-Api-Key`.
331
661
  important: |
332
- 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.
333
- body: |
334
- - `'camelcase'` transforms parameter names to camelCase.
662
+ Set the same `paramsCasing` on every plugin that touches operation parameters (`@kubb/plugin-ts`, `@kubb/plugin-client`, `@kubb/plugin-react-query`, `@kubb/plugin-faker`, `@kubb/plugin-mcp`). Mismatched casing causes type errors between generated layers.
335
663
  examples:
336
- - name: With paramsCasing camelcase
664
+ - name: "With paramsCasing: 'camelcase'"
337
665
  files:
338
- - lang: typescript
666
+ - name: Generated client
667
+ lang: typescript
668
+ twoslash: false
339
669
  code: |
340
- // Function parameters use camelCase
670
+ // Function takes camelCase parameters
341
671
  export async function deletePet(
342
- petId: DeletePetPathParams['petId'], // ✓ camelCase
672
+ petId: DeletePetPathParams['petId'],
343
673
  headers?: DeletePetHeaderParams,
344
- config: Partial<RequestConfig> = {}
674
+ config: Partial<RequestConfig> = {},
345
675
  ) {
346
- // Automatically maps back to original name for the API
676
+ // ...mapped back to the original API name internally
347
677
  const pet_id = petId
348
678
 
349
- return fetch({
679
+ return client({
350
680
  method: 'DELETE',
351
- url: `/pet/${pet_id}`, // Uses original API parameter name
352
- ...
681
+ url: `/pet/${pet_id}`,
682
+ ...config,
353
683
  })
354
684
  }
685
+ - name: Caller
686
+ lang: typescript
355
687
  twoslash: false
688
+ code: |
689
+ await deletePet(42)
356
690
  - name: Without paramsCasing
357
691
  files:
358
- - lang: typescript
692
+ - name: Generated client
693
+ lang: typescript
694
+ twoslash: false
359
695
  code: |
360
- // Parameters use original API naming
696
+ // Function parameters mirror the OpenAPI spec
361
697
  export async function deletePet(
362
- pet_id: DeletePetPathParams['pet_id'], // Original naming
698
+ pet_id: DeletePetPathParams['pet_id'],
363
699
  headers?: DeletePetHeaderParams,
364
- config: Partial<RequestConfig> = {}
700
+ config: Partial<RequestConfig> = {},
365
701
  ) {
366
- return fetch({
702
+ return client({
367
703
  method: 'DELETE',
368
704
  url: `/pet/${pet_id}`,
369
- ...
705
+ ...config,
370
706
  })
371
707
  }
372
- twoslash: false
373
708
  tip: |
374
- 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.
709
+ Callers write friendly camelCase names. The generated client maps them back to whatever the API expects (snake_case path params, kebab-case headers, ...).
375
710
  - name: pathParamsType
376
711
  type: "'object' | 'inline'"
377
712
  required: false
378
713
  default: "'inline'"
379
- description: Defines how pathParams are passed to generated functions.
380
- body: |
381
- - `'object'` returns pathParams as an object.
382
- - `'inline'` returns pathParams as comma-separated params.
714
+ description: |
715
+ How URL path parameters appear in the generated function signature. Affects only path params; query/header params follow `paramsType`.
716
+
717
+ - `'inline'` (default) each path param is a positional argument: `getPetById(petId)`.
718
+ - `'object'` — path params are wrapped in a single object: `getPetById({ petId })`.
383
719
  examples:
384
- - name: object
720
+ - name: "'inline' (default)"
385
721
  files:
386
722
  - lang: typescript
723
+ twoslash: false
387
724
  code: |
388
725
  export async function getPetById(
389
- { petId }: GetPetByIdPathParams,
726
+ petId: GetPetByIdPathParams,
390
727
  ) {
391
- ...
728
+ // ...
392
729
  }
393
- twoslash: false
394
- - name: inline
730
+ - name: "'object'"
395
731
  files:
396
732
  - lang: typescript
733
+ twoslash: false
397
734
  code: |
398
735
  export async function getPetById(
399
- petId: GetPetByIdPathParams,
736
+ { petId }: GetPetByIdPathParams,
400
737
  ) {
401
- ...
738
+ // ...
402
739
  }
403
- twoslash: false
404
740
  - name: parser
405
- type: "'client' | 'zod'"
741
+ type: false | 'zod'
406
742
  required: false
407
- default: "'client'"
408
- description: Parser used before returning data.
409
- body: |
410
- - `'zod'` uses `@kubb/plugin-zod` to parse data.
411
- - `'client'` returns data without parsing.
743
+ default: 'false'
744
+ description: |
745
+ Runtime validator applied to the response body before it is returned to the caller.
746
+
747
+ - `false` (default) no validation. The client has no runtime parser; the response is cast to the generated TypeScript type and returned as-is. Fastest path, trusts the API.
748
+ - `'zod'` — pipes the response through the Zod schema produced by `@kubb/plugin-zod`. Catches mismatches between spec and API at runtime, at the cost of a parse on every call.
749
+
750
+ Use `'zod'` when you want a defensive boundary against drift between your OpenAPI spec and the live API. Requires `@kubb/plugin-zod` in the plugins list.
751
+ examples:
752
+ - name: Validate responses with Zod
753
+ files:
754
+ - lang: typescript
755
+ twoslash: false
756
+ code: |
757
+ import { defineConfig } from 'kubb'
758
+ import { pluginClient } from '@kubb/plugin-client'
759
+ import { pluginTs } from '@kubb/plugin-ts'
760
+ import { pluginZod } from '@kubb/plugin-zod'
761
+
762
+ export default defineConfig({
763
+ input: { path: './petStore.yaml' },
764
+ output: { path: './src/gen' },
765
+ plugins: [
766
+ pluginTs(),
767
+ pluginZod(),
768
+ pluginClient({
769
+ parser: 'zod',
770
+ }),
771
+ ],
772
+ })
412
773
  - name: infinite
413
774
  type: Infinite | false
414
775
  required: false
415
- default: "false"
776
+ default: 'false'
416
777
  description: |
417
- Generate an `infiniteQuery` hook for paginated queries. Pass `false` to disable.
778
+ Enables `useInfiniteQuery` hooks for cursor- or page-based pagination. Pass an object to configure how the cursor is read from the response; pass `false` (default) to skip infinite query generation.
418
779
  codeBlock:
419
780
  lang: typescript
420
- title: Infinite
781
+ title: Infinite type
421
782
  code: |
422
783
  type Infinite = {
423
- /**
424
- * Specify the params key used for `pageParam`.
425
- * @default `'id'`
426
- */
784
+ /** Query-param key used as the page cursor. Defaults to `'id'`. */
427
785
  queryParam: string
428
- /**
429
- * Which field of the data will be used, set it to undefined when no cursor is known.
430
- * @deprecated Use `nextParam` and `previousParam` instead for more flexible pagination handling.
431
- */
432
- cursorParam?: string | undefined
433
- /**
434
- * Which field of the data will be used to get the cursor for the next page.
435
- * Supports dot notation (e.g. 'pagination.next.id') or array path (e.g. ['pagination', 'next', 'id']) to access nested fields.
436
- */
437
- nextParam?: string | string[] | undefined
438
- /**
439
- * Which field of the data will be used to get the cursor for the previous page.
440
- * Supports dot notation (e.g. 'pagination.prev.id') or array path (e.g. ['pagination', 'prev', 'id']) to access nested fields.
441
- */
442
- previousParam?: string | string[] | undefined
443
- /**
444
- * The initial value, the value of the first page.
445
- * @default `0`
446
- */
786
+ /** @deprecated Use `nextParam` / `previousParam` instead. */
787
+ cursorParam?: string
788
+ /** Path to the next-page cursor in the response. Dot or array form. */
789
+ nextParam?: string | string[]
790
+ /** Path to the previous-page cursor in the response. Dot or array form. */
791
+ previousParam?: string | string[]
792
+ /** Value of `pageParam` for the first page. Defaults to `0`. */
447
793
  initialPageParam: unknown
448
794
  } | false
449
795
  properties:
@@ -451,42 +797,59 @@ options:
451
797
  type: string
452
798
  required: false
453
799
  default: "'id'"
454
- description: Specify the params key used for `pageParam`.
800
+ description: Name of the query parameter that holds the page cursor (Kubb passes `pageParam` into this query key).
455
801
  - name: initialPageParam
456
802
  type: unknown
457
803
  required: false
458
- default: "0"
459
- description: Specify the initial page param value.
804
+ default: '0'
805
+ description: Initial value for `pageParam` on the first fetch.
460
806
  - name: cursorParam
461
807
  type: string | undefined
462
808
  required: false
463
809
  deprecated:
464
- note: "`cursorParam` is deprecated. Use `nextParam` and `previousParam` instead for more flexible pagination handling."
465
- description: Which field of the data will be used, set it to undefined when no cursor is known.
810
+ note: '`cursorParam` is deprecated. Use `nextParam` and `previousParam` for richer pagination control.'
811
+ description: Path to the cursor field on the response. Leave undefined when the cursor is not known.
466
812
  - name: nextParam
467
813
  type: string | string[] | undefined
468
814
  required: false
469
815
  description: |
470
- Which field of the data will be used to get the cursor for the next page.
471
-
472
- Supports dot notation (e.g. `'pagination.next.id'`) or array path (e.g. `['pagination', 'next', 'id']`) to access nested fields.
816
+ Path to the next-page cursor on the response. Supports dot notation (`'pagination.next.id'`) or array form (`['pagination', 'next', 'id']`).
473
817
  - name: previousParam
474
818
  type: string | string[] | undefined
475
819
  required: false
476
820
  description: |
477
- Which field of the data will be used to get the cursor for the previous page.
821
+ Path to the previous-page cursor on the response. Supports dot notation (`'pagination.prev.id'`) or array form (`['pagination', 'prev', 'id']`).
822
+ examples:
823
+ - name: Cursor pagination
824
+ files:
825
+ - lang: typescript
826
+ twoslash: false
827
+ code: |
828
+ import { defineConfig } from 'kubb'
829
+ import { pluginReactQuery } from '@kubb/plugin-react-query'
478
830
 
479
- Supports dot notation (e.g. `'pagination.prev.id'`) or array path (e.g. `['pagination', 'prev', 'id']`) to access nested fields.
831
+ export default defineConfig({
832
+ input: { path: './petStore.yaml' },
833
+ output: { path: './src/gen' },
834
+ plugins: [
835
+ pluginReactQuery({
836
+ infinite: {
837
+ queryParam: 'cursor',
838
+ initialPageParam: null,
839
+ nextParam: 'pagination.next.cursor',
840
+ previousParam: 'pagination.prev.cursor',
841
+ },
842
+ }),
843
+ ],
844
+ })
480
845
  - name: query
481
846
  type: Query
482
847
  required: false
483
848
  description: |
484
- Override some `useQuery` behaviors.
485
-
486
- To disable the creation of hooks pass `false`, this will result in only creating `queryOptions`.
849
+ Configures the query hooks. Pass `false` to skip generating hooks entirely and only emit `queryOptions(...)` helpers — useful when you want to call `useQuery` yourself in app code.
487
850
  codeBlock:
488
851
  lang: typescript
489
- title: Query
852
+ title: Query type
490
853
  code: |
491
854
  type Query = {
492
855
  methods: Array<HttpMethod>
@@ -497,32 +860,49 @@ options:
497
860
  type: Array<HttpMethod>
498
861
  required: false
499
862
  default: "['get']"
500
- description: Define which HttpMethods can be used for queries.
863
+ description: |
864
+ HTTP methods treated as queries. Operations using one of these methods generate a `useQuery`-style hook (or `queryOptions` helper) instead of a mutation.
865
+
866
+ Defaults to `['get']`. Add other methods (for example `'head'`) only when your API uses them for cache-friendly reads.
867
+ examples:
868
+ - name: Allow HEAD as a query method
869
+ files:
870
+ - lang: typescript
871
+ twoslash: false
872
+ code: |
873
+ import { defineConfig } from 'kubb'
874
+ import { pluginReactQuery } from '@kubb/plugin-react-query'
875
+
876
+ export default defineConfig({
877
+ input: { path: './petStore.yaml' },
878
+ output: { path: './src/gen' },
879
+ plugins: [
880
+ pluginReactQuery({
881
+ query: { methods: ['get', 'head'] },
882
+ }),
883
+ ],
884
+ })
501
885
  - name: importPath
502
886
  type: string
503
887
  required: false
504
888
  default: "'@tanstack/react-query'"
505
889
  description: |
506
- Path to the `useQuery` that will be used to do the `useQuery` functionality.
507
- It will be used as `import { useQuery } from '${hook.importPath}'`.
508
- 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.
890
+ Module specifier used in the `import { useQuery } from '...'` statement at the top of every generated hook file. Use this to point at your own re-export of TanStack Query (for example a wrapper that injects a default `queryClient`).
509
891
  - name: queryKey
510
- type: "(props: { operation: Operation; schemas: OperationSchemas }) => unknown[]"
892
+ type: '(props: { operation: Operation; schemas: OperationSchemas }) => unknown[]'
511
893
  required: false
512
894
  description: |
513
- Customize the queryKey that will be used for the query.
895
+ Builds the `queryKey` for each generated hook. Use this to add a version namespace, swap to operation IDs, or shape keys to match an existing `queryClient.invalidateQueries` strategy.
896
+
897
+ The callback receives:
514
898
 
515
- The function receives an object with:
516
- - `operation`: The OpenAPI operation object with methods like `getTags()`, `getOperationId()`, etc.
517
- - `schemas`: An object containing operation schemas including `pathParams`, `queryParams`, `request`, `response`, etc.
899
+ - `operation` the OpenAPI operation (`getTags()`, `getOperationId()`, ...).
900
+ - `schemas` operation schemas including `pathParams`, `queryParams`, `request`, `response`.
518
901
  warning: |
519
- When using a string you need to use `JSON.stringify`.
902
+ String values are inlined verbatim into generated code. Wrap any string you want emitted as a literal in `JSON.stringify(...)`.
520
903
  details:
521
- - title: Using tags and path parameters
522
- body: |-
523
- Generate a queryKey with operation tags and path parameters:
524
-
525
- For a GET operation with tags `["user"]` and path parameter `username`, this generates:
904
+ - title: Keys from tags and path parameters
905
+ body: "Build a key from the operation's first tag plus its path parameters. For a `GET /user/{username}` operation with the `user` tag, this generates:"
526
906
  codeBlock:
527
907
  - lang: typescript
528
908
  code: |-
@@ -530,12 +910,13 @@ options:
530
910
  import { pluginReactQuery } from '@kubb/plugin-react-query'
531
911
 
532
912
  export default defineConfig({
533
- // ...
913
+ input: { path: './petStore.yaml' },
914
+ output: { path: './src/gen' },
534
915
  plugins: [
535
916
  pluginReactQuery({
536
917
  queryKey: ({ operation, schemas }) => {
537
- const tags = operation.getTags().map(tag => JSON.stringify(tag.name))
538
- const pathParams = schemas.pathParams?.keys || []
918
+ const tags = operation.getTags().map((tag) => JSON.stringify(tag.name))
919
+ const pathParams = schemas.pathParams?.keys ?? []
539
920
  return [...tags, ...pathParams]
540
921
  },
541
922
  }),
@@ -543,21 +924,20 @@ options:
543
924
  })
544
925
  - lang: typescript
545
926
  code: |-
546
- export const getUserByNameQueryKey = ({ username }: { username: GetUserByNamePathParams["username"] }) =>
927
+ export const getUserByNameQueryKey = ({ username }: { username: GetUserByNamePathParams['username'] }) =>
547
928
  ['user', username] as const
548
- - title: Using the default transformer
549
- body: |-
550
- You can extend the default queryKey transformer:
551
-
552
- This prepends a version to the default queryKey:
929
+ - title: Extend the default transformer
930
+ body: 'Prepend a version prefix to the default query key:'
553
931
  codeBlock:
554
932
  - lang: typescript
555
933
  code: |-
934
+ import { defineConfig } from 'kubb'
556
935
  import { pluginReactQuery } from '@kubb/plugin-react-query'
557
936
  import { QueryKey } from '@kubb/plugin-react-query/components'
558
937
 
559
938
  export default defineConfig({
560
- // ...
939
+ input: { path: './petStore.yaml' },
940
+ output: { path: './src/gen' },
561
941
  plugins: [
562
942
  pluginReactQuery({
563
943
  queryKey: (props) => {
@@ -571,43 +951,43 @@ options:
571
951
  code: |-
572
952
  export const findPetsByTagsQueryKey = (params?: FindPetsByTagsQueryParams) =>
573
953
  ['v5', { url: '/pet/findByTags' }, ...(params ? [params] : [])] as const
574
- - title: Using operation ID
575
- body: "Create a simple queryKey using the operation ID:"
954
+ - title: Key from operationId
955
+ body: Use the operationId as the only key. Smallest possible key.
576
956
  codeBlock:
577
957
  lang: typescript
578
958
  code: |-
959
+ import { defineConfig } from 'kubb'
579
960
  import { pluginReactQuery } from '@kubb/plugin-react-query'
580
961
 
581
962
  export default defineConfig({
582
- // ...
963
+ input: { path: './petStore.yaml' },
964
+ output: { path: './src/gen' },
583
965
  plugins: [
584
966
  pluginReactQuery({
585
- queryKey: ({ operation }) => {
586
- return [JSON.stringify(operation.getOperationId())]
587
- },
967
+ queryKey: ({ operation }) => [JSON.stringify(operation.getOperationId())],
588
968
  }),
589
969
  ],
590
970
  })
591
- - title: Conditional keys based on parameters
592
- body: "Include query parameters when they exist:"
971
+ - title: Conditional keys based on params
972
+ body: 'Include query params in the key only when they are present:'
593
973
  codeBlock:
594
974
  lang: typescript
595
975
  code: |-
976
+ import { defineConfig } from 'kubb'
596
977
  import { pluginReactQuery } from '@kubb/plugin-react-query'
597
978
 
598
979
  export default defineConfig({
599
- // ...
980
+ input: { path: './petStore.yaml' },
981
+ output: { path: './src/gen' },
600
982
  plugins: [
601
983
  pluginReactQuery({
602
984
  queryKey: ({ operation, schemas }) => {
603
- const keys = [JSON.stringify(operation.getOperationId())]
985
+ const keys: unknown[] = [JSON.stringify(operation.getOperationId())]
604
986
 
605
- // Add path parameter values (without quotes, so they reference the variables)
606
987
  if (schemas.pathParams?.keys) {
607
988
  keys.push(...schemas.pathParams.keys)
608
989
  }
609
990
 
610
- // Add query params conditionally (the string gets embedded as code)
611
991
  if (schemas.queryParams?.name) {
612
992
  keys.push('...(params ? [params] : [])')
613
993
  }
@@ -620,17 +1000,18 @@ options:
620
1000
  - name: suspense
621
1001
  type: object | false
622
1002
  required: false
623
- description: When set, a `suspenseQuery` hook will be added. This will only work for v5 and react.
1003
+ description: |
1004
+ Adds `useSuspenseQuery` hooks alongside the regular `useQuery` ones. Pass an empty object (`{}`) to enable; omit or set to `false` to skip.
1005
+
1006
+ Suspense queries throw promises while loading and require a `<Suspense>` boundary in the React tree. TanStack Query v5+ only.
624
1007
  - name: mutation
625
1008
  type: Mutation
626
1009
  required: false
627
1010
  description: |
628
- Override some `useMutation` behaviors.
629
-
630
- To disable queries pass `false`.
1011
+ Configures mutation hooks. Set to `false` to skip mutation generation entirely.
631
1012
  codeBlock:
632
1013
  lang: typescript
633
- title: Mutation
1014
+ title: Mutation type
634
1015
  code: |
635
1016
  type Mutation = {
636
1017
  methods: Array<HttpMethod>
@@ -640,32 +1021,52 @@ options:
640
1021
  - name: methods
641
1022
  type: Array<HttpMethod>
642
1023
  required: false
643
- default: "['post', 'put', 'delete']"
644
- description: Define which HttpMethods can be used for mutations.
1024
+ default: "['post', 'put', 'patch', 'delete']"
1025
+ description: |
1026
+ HTTP methods treated as mutations. Operations using one of these methods generate a `useMutation`-style hook instead of a query.
1027
+
1028
+ Defaults to `['post', 'put', 'patch', 'delete']`. Narrow the list if your API uses one of these methods for reads.
1029
+ examples:
1030
+ - name: Treat only POST and PUT as mutations
1031
+ files:
1032
+ - lang: typescript
1033
+ twoslash: false
1034
+ code: |
1035
+ import { defineConfig } from 'kubb'
1036
+ import { pluginReactQuery } from '@kubb/plugin-react-query'
1037
+
1038
+ export default defineConfig({
1039
+ input: { path: './petStore.yaml' },
1040
+ output: { path: './src/gen' },
1041
+ plugins: [
1042
+ pluginReactQuery({
1043
+ mutation: { methods: ['post', 'put'] },
1044
+ }),
1045
+ ],
1046
+ })
645
1047
  - name: importPath
646
1048
  type: string
647
1049
  required: false
648
1050
  default: "'@tanstack/react-query'"
649
1051
  description: |
650
- Path to the `useMutation` that will be used to do the `useMutation` functionality.
651
- It will be used as `import { useMutation } from '${hook.importPath}'`.
652
- 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.
1052
+ Module specifier used in the `import { useMutation } from '...'` statement at the top of every generated hook file. Useful for routing through your own wrapper.
653
1053
  - name: mutationKey
654
- type: "(props: { operation: Operation; schemas: OperationSchemas }) => unknown[]"
1054
+ type: '(props: { operation: Operation; schemas: OperationSchemas }) => unknown[]'
655
1055
  required: false
656
- description: Customize the mutationKey.
1056
+ description: |
1057
+ Builds the `mutationKey` for each mutation hook. Useful when you batch invalidations or read mutation state via `useMutationState`.
657
1058
  warning: |
658
- When using a string you need to use `JSON.stringify`.
1059
+ String values are inlined verbatim into generated code. Wrap literal strings in `JSON.stringify(...)`.
659
1060
  - name: customOptions
660
1061
  type: CustomOptions
661
1062
  required: false
662
1063
  description: |
663
- When set, a custom hook will be used to customize the options of the generated hooks.
1064
+ Wires every generated hook through a user-supplied function that returns extra options (`onSuccess`, `onError`, `select`, ...). The plugin also emits a `HookOptions` type so your wrapper stays in sync with the generated hooks.
664
1065
 
665
- It will also generate a `HookOptions` type that can be used to type the custom options of each hook for type-safety.
1066
+ Use this to centralize cache invalidation, error toasts, or analytics in one place instead of repeating them at every call site.
666
1067
  codeBlock:
667
1068
  lang: typescript
668
- title: CustomOptions
1069
+ title: CustomOptions type
669
1070
  code: |
670
1071
  type CustomOptions = {
671
1072
  importPath: string
@@ -676,251 +1077,397 @@ options:
676
1077
  type: string
677
1078
  required: true
678
1079
  description: |
679
- Path to the hook that will be used to customize the hook options.
680
- It will be used as `import ${customOptions.name} from '${customOptions.importPath}'`.
681
- 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.
1080
+ Module specifier of your custom-options hook. Imported as `import ${name} from '${importPath}'`. Use a relative path from the generated file or a bare specifier.
682
1081
  - name: name
683
1082
  type: string
684
1083
  required: false
685
1084
  default: "'useCustomHookOptions'"
686
1085
  description: |
687
- Name of the exported hook that will be used to customize the hook options.
688
- It will be used as `import ${customOptions.name} from '${customOptions.importPath}'`.
689
- If not provided, it defaults to `useCustomHookOptions`.
1086
+ Exported function name of your custom-options hook. Imported as `import ${name} from '${importPath}'`.
690
1087
  details:
691
- - title: Add custom hook options to invalidate queries
1088
+ - title: Centralised cache invalidation
692
1089
  codeBlock:
693
1090
  lang: typescript
694
- title: useCustomHookOptions.ts
1091
+ title: src/useCustomHookOptions.ts
695
1092
  code: |-
1093
+ import { useQueryClient, type QueryClient } from '@tanstack/react-query'
1094
+ import type { HookOptions } from '../gen/hookOptions'
1095
+ import { getUserByNameQueryKey } from '../gen/hooks/userController/useGetUserByNameHook'
1096
+
696
1097
  function getCustomHookOptions({ queryClient }: { queryClient: QueryClient }): Partial<HookOptions> {
697
1098
  return {
698
1099
  useUpdatePetHook: {
699
1100
  onSuccess: () => {
700
- // Invalidate queries using a constant
701
1101
  void queryClient.invalidateQueries({ queryKey: ['pet'] })
702
1102
  },
703
1103
  },
704
1104
  useDeletePetHook: {
705
- onSuccess: (_data, variables, _onMutateResult, _context) => {
706
- // Invalidate queries using the provided variables
1105
+ onSuccess: (_data, variables) => {
707
1106
  void queryClient.invalidateQueries({ queryKey: ['pet', variables.pet_id] })
708
1107
  },
709
1108
  },
710
1109
  useUpdateUserHook: {
711
- onSuccess: (_data, variables, _onMutateResult, _context) => {
712
- // Invalidate queries using the provided query key generator function
713
- void queryClient.invalidateQueries({ queryKey: getUserByNameQueryKey({ username: variables.username }) })
1110
+ onSuccess: (_data, variables) => {
1111
+ void queryClient.invalidateQueries({
1112
+ queryKey: getUserByNameQueryKey({ username: variables.username }),
1113
+ })
714
1114
  },
715
1115
  },
716
- // Add more custom hook options here...
717
1116
  }
718
1117
  }
719
1118
 
720
- export function useCustomHookOptions<T extends keyof HookOptions>({ hookName, operationId }: { hookName: T; operationId: string }): HookOptions[T] {
1119
+ export function useCustomHookOptions<T extends keyof HookOptions>({
1120
+ hookName,
1121
+ }: {
1122
+ hookName: T
1123
+ operationId: string
1124
+ }): HookOptions[T] {
721
1125
  const queryClient = useQueryClient()
722
1126
  const customOptions = getCustomHookOptions({ queryClient })
723
1127
  return customOptions[hookName] ?? {}
724
1128
  }
725
- - title: Add custom hook options to implement custom error handling
1129
+ - title: App-wide error reporting
726
1130
  codeBlock:
727
1131
  lang: typescript
728
- title: useCustomHookOptions.ts
1132
+ title: src/useCustomHookOptions.ts
729
1133
  code: |-
730
- function getCustomHookOptions({queryClient}: { queryClient: QueryClient }): Partial<HookOptions> {
1134
+ import { toast } from 'sonner'
1135
+ import { useQueryClient, type QueryClient } from '@tanstack/react-query'
1136
+ import type { HookOptions } from '../gen/hookOptions'
1137
+
1138
+ function getCustomHookOptions({ queryClient }: { queryClient: QueryClient }): Partial<HookOptions> {
731
1139
  return {
732
1140
  useUpdatePetHook: {
733
- onError: (error, _variables, _onMutateResult, _context) => {
734
- // Log the error
735
- console.error(`Failed to update pet:`, error);
1141
+ onError: (error) => {
1142
+ console.error('Failed to update pet:', error)
736
1143
  },
737
1144
  },
738
1145
  useDeletePetHook: {
739
- onError: (_error, variables, _onMutateResult, _context) => {
740
- // Show a toast notification
741
- toast.error(`Failed to delete pet with id '${variables.pet_id}'`);
1146
+ onError: (_error, variables) => {
1147
+ toast.error(`Failed to delete pet with id '${variables.pet_id}'`)
742
1148
  },
743
1149
  },
744
- useUpdateUserHook: {
745
- onError: (_error, variables, _onMutateResult, _context) => {
746
- // Post the error to a custom analytics service
747
- postEvent('user_updated_failed', { username: variables.username, message: error.message, date: Date.now() })
748
- },
749
- },
750
- // Add more custom hook options here...
751
1150
  }
752
1151
  }
753
1152
 
754
- export function useCustomHookOptions<T extends keyof HookOptions>({ hookName, operationId }: { hookName: T; operationId: string }): HookOptions[T] {
1153
+ export function useCustomHookOptions<T extends keyof HookOptions>({
1154
+ hookName,
1155
+ }: {
1156
+ hookName: T
1157
+ operationId: string
1158
+ }): HookOptions[T] {
755
1159
  const queryClient = useQueryClient()
756
- const customOptions = getCustomHookOptions({queryClient})
1160
+ const customOptions = getCustomHookOptions({ queryClient })
757
1161
  return customOptions[hookName] ?? {}
758
1162
  }
759
1163
  - name: include
760
1164
  type: Array<Include>
761
1165
  required: false
762
- description: Array containing include parameters to include tags, operations, methods, paths, or content types.
1166
+ description: |
1167
+ Restricts generation to operations that match at least one entry in the list. Anything not matched is skipped.
1168
+
1169
+ Each entry filters by one of:
1170
+
1171
+ - `tag` — the operation's first tag in the OpenAPI spec.
1172
+ - `operationId` — the operation's `operationId`.
1173
+ - `path` — the URL pattern (`'/pet/{petId}'`).
1174
+ - `method` — HTTP method (`'get'`, `'post'`, ...).
1175
+ - `contentType` — the media type of the request body.
1176
+
1177
+ `pattern` accepts either a string (exact match) or a `RegExp` for fuzzy matches.
763
1178
  codeBlock:
764
1179
  lang: typescript
765
- title: Include
1180
+ title: Type definition
766
1181
  code: |
767
1182
  export type Include = {
768
1183
  type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType'
769
1184
  pattern: string | RegExp
770
1185
  }
1186
+ examples:
1187
+ - name: Only the pet tag
1188
+ files:
1189
+ - name: kubb.config.ts
1190
+ lang: typescript
1191
+ twoslash: false
1192
+ code: |
1193
+ import { defineConfig } from 'kubb'
1194
+ import { pluginTs } from '@kubb/plugin-ts'
1195
+
1196
+ export default defineConfig({
1197
+ input: { path: './petStore.yaml' },
1198
+ output: { path: './src/gen' },
1199
+ plugins: [
1200
+ pluginTs({
1201
+ include: [
1202
+ { type: 'tag', pattern: 'pet' },
1203
+ ],
1204
+ }),
1205
+ ],
1206
+ })
1207
+ - name: Only GET operations under /pet
1208
+ files:
1209
+ - name: kubb.config.ts
1210
+ lang: typescript
1211
+ twoslash: false
1212
+ code: |
1213
+ import { defineConfig } from 'kubb'
1214
+ import { pluginTs } from '@kubb/plugin-ts'
1215
+
1216
+ export default defineConfig({
1217
+ input: { path: './petStore.yaml' },
1218
+ output: { path: './src/gen' },
1219
+ plugins: [
1220
+ pluginTs({
1221
+ include: [
1222
+ { type: 'method', pattern: 'get' },
1223
+ { type: 'path', pattern: /^\/pet/ },
1224
+ ],
1225
+ }),
1226
+ ],
1227
+ })
771
1228
  - name: exclude
772
1229
  type: Array<Exclude>
773
1230
  required: false
774
- description: Array containing exclude parameters to exclude or skip tags, operations, methods, paths, or content types.
1231
+ description: |
1232
+ Skips any operation that matches at least one entry in the list. The opposite of `include`.
1233
+
1234
+ Each entry filters by one of:
1235
+
1236
+ - `tag` — the operation's first tag.
1237
+ - `operationId` — the operation's `operationId`.
1238
+ - `path` — the URL pattern (`'/pet/{petId}'`).
1239
+ - `method` — HTTP method (`'get'`, `'post'`, ...).
1240
+ - `contentType` — the media type of the request body.
1241
+
1242
+ `pattern` accepts a plain string or a `RegExp`. When both `include` and `exclude` are set, `exclude` wins.
775
1243
  codeBlock:
776
1244
  lang: typescript
777
- title: Exclude
1245
+ title: Type definition
778
1246
  code: |
779
1247
  export type Exclude = {
780
1248
  type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType'
781
1249
  pattern: string | RegExp
782
1250
  }
1251
+ examples:
1252
+ - name: Skip everything under the store tag
1253
+ files:
1254
+ - name: kubb.config.ts
1255
+ lang: typescript
1256
+ twoslash: false
1257
+ code: |
1258
+ import { defineConfig } from 'kubb'
1259
+ import { pluginTs } from '@kubb/plugin-ts'
1260
+
1261
+ export default defineConfig({
1262
+ input: { path: './petStore.yaml' },
1263
+ output: { path: './src/gen' },
1264
+ plugins: [
1265
+ pluginTs({
1266
+ exclude: [
1267
+ { type: 'tag', pattern: 'store' },
1268
+ ],
1269
+ }),
1270
+ ],
1271
+ })
1272
+ - name: Skip a specific operation and all delete methods
1273
+ files:
1274
+ - name: kubb.config.ts
1275
+ lang: typescript
1276
+ twoslash: false
1277
+ code: |
1278
+ import { defineConfig } from 'kubb'
1279
+ import { pluginTs } from '@kubb/plugin-ts'
1280
+
1281
+ export default defineConfig({
1282
+ input: { path: './petStore.yaml' },
1283
+ output: { path: './src/gen' },
1284
+ plugins: [
1285
+ pluginTs({
1286
+ exclude: [
1287
+ { type: 'operationId', pattern: 'deletePet' },
1288
+ { type: 'method', pattern: 'delete' },
1289
+ ],
1290
+ }),
1291
+ ],
1292
+ })
783
1293
  - name: override
784
1294
  type: Array<Override>
785
1295
  required: false
786
- description: Array containing override parameters to override `options` based on tags, operations, methods, paths, or content types.
1296
+ description: |
1297
+ Applies a different set of plugin options to operations that match a pattern. Use this when most of your API should follow the global config, but a handful of endpoints need different treatment.
1298
+
1299
+ Each entry has the same `type` and `pattern` shape as `include`/`exclude`, plus an `options` object that overrides the plugin's options for matched operations.
1300
+
1301
+ Entries are evaluated top to bottom. The first matching entry's `options` is merged onto the plugin defaults; later entries do not stack.
787
1302
  codeBlock:
788
1303
  lang: typescript
789
- title: Override
1304
+ title: Type definition
790
1305
  code: |
791
1306
  export type Override = {
792
1307
  type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType'
793
1308
  pattern: string | RegExp
794
1309
  options: PluginOptions
795
1310
  }
1311
+ examples:
1312
+ - name: Use a different enum style for the user tag
1313
+ files:
1314
+ - name: kubb.config.ts
1315
+ lang: typescript
1316
+ twoslash: false
1317
+ code: |
1318
+ import { defineConfig } from 'kubb'
1319
+ import { pluginTs } from '@kubb/plugin-ts'
1320
+
1321
+ export default defineConfig({
1322
+ input: { path: './petStore.yaml' },
1323
+ output: { path: './src/gen' },
1324
+ plugins: [
1325
+ pluginTs({
1326
+ enumType: 'asConst',
1327
+ override: [
1328
+ {
1329
+ type: 'tag',
1330
+ pattern: 'user',
1331
+ options: { enumType: 'literal' },
1332
+ },
1333
+ ],
1334
+ }),
1335
+ ],
1336
+ })
796
1337
  - name: generators
797
1338
  type: Array<Generator<PluginReactQuery>>
798
1339
  required: false
799
1340
  experimental: true
800
1341
  description: |
801
- Define additional generators next to the built-in generators.
1342
+ Adds custom generators that run alongside the plugin's built-in generators. Each generator can emit additional files or post-process existing ones using the plugin's AST and options.
802
1343
 
803
- See [Generators](https://kubb.dev/docs/5.x/guides/creating-plugins) for more information on how to use generators.
804
- - name: transformers
805
- type: "{ name?: (name: string, type?: ResolveType) => string }"
806
- required: false
807
- description: Customize the generated names based on the type provided by the plugin.
808
- properties:
809
- - name: name
810
- type: "(name: string, type?: ResolveType) => string"
811
- required: false
812
- description: Customize the names based on the type that is provided by the plugin.
813
- codeBlock:
814
- lang: typescript
815
- code: |
816
- type ResolveType = 'file' | 'function' | 'type' | 'const'
1344
+ Use this when you need output the plugin does not produce out of the box (a custom client wrapper, an extra index, a metadata file). For end-to-end guidance, see [Creating plugins](https://kubb.dev/docs/5.x/guides/creating-plugins).
1345
+ warning: |
1346
+ Generators are an experimental, low-level API. The signature may change between minor releases.
817
1347
  - name: resolver
818
1348
  type: Partial<ResolverReactQuery> & ThisType<ResolverReactQuery>
819
1349
  required: false
820
1350
  description: |
821
- 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.
1351
+ Overrides how the plugin builds names and paths for generated files and symbols. Use this to add prefixes, suffixes, or to swap the casing strategy without forking the plugin.
1352
+
1353
+ Only override the methods you want to change. Anything you omit falls back to the plugin's default resolver. A method that returns `null` or `undefined` also falls back.
822
1354
 
823
- `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.
1355
+ Inside each method, `this` is bound to the full resolver, so you can call `this.default(name, 'function')` to delegate to the built-in implementation.
824
1356
  body: |
825
- Each plugin ships a built-in resolver:
1357
+ Each plugin ships with a default resolver:
826
1358
 
827
1359
  | Plugin | Default resolver |
828
1360
  | --- | --- |
829
1361
  | `@kubb/plugin-ts` | `resolverTs` |
830
1362
  | `@kubb/plugin-zod` | `resolverZod` |
1363
+ | `@kubb/plugin-faker` | `resolverFaker` |
831
1364
  | `@kubb/plugin-cypress` | `resolverCypress` |
1365
+ | `@kubb/plugin-msw` | `resolverMsw` |
832
1366
  | `@kubb/plugin-mcp` | `resolverMcp` |
1367
+ | `@kubb/plugin-client` | `resolverClient` |
833
1368
  codeBlock:
834
1369
  lang: typescript
835
- title: Custom resolver (plugin-ts example)
1370
+ title: Add an Api prefix to every name
836
1371
  code: |
1372
+ import { defineConfig } from 'kubb'
837
1373
  import { pluginTs } from '@kubb/plugin-ts'
838
1374
 
839
- pluginTs({
840
- resolver: {
841
- resolveName(name) {
842
- // Prefix every operation-derived name; falls back for names where
843
- // this returns null/undefined.
844
- return `Api${this.default(name, 'function')}`
845
- },
846
- },
1375
+ export default defineConfig({
1376
+ input: { path: './petStore.yaml' },
1377
+ output: { path: './src/gen' },
1378
+ plugins: [
1379
+ pluginTs({
1380
+ resolver: {
1381
+ resolveName(name) {
1382
+ return `Api${this.default(name, 'function')}`
1383
+ },
1384
+ },
1385
+ }),
1386
+ ],
847
1387
  })
848
1388
  tip: |
849
- Use `resolver` for fine-grained control over naming conventions.
1389
+ Use `resolver` for naming and file-location tweaks. For changing the AST nodes themselves (e.g. stripping descriptions), use `transformer` instead.
850
1390
  - name: transformer
851
1391
  type: Visitor
852
1392
  required: false
853
1393
  description: |
854
- 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.
1394
+ Modifies AST nodes before they are printed to source code. Use this when you need to rewrite operation IDs, drop descriptions, or change schema metadata without forking the generator.
855
1395
 
856
- Visitor methods receive the node and a context object. Return a modified node to replace it, or return `undefined`/`void` to leave it unchanged.
1396
+ Each visitor method (e.g. `schema`, `operation`) receives the node and a context object. Return a new node to replace it, or return `undefined` to leave it untouched. Methods you omit keep the plugin's default behavior.
857
1397
  examples:
858
1398
  - name: Strip descriptions before printing
859
1399
  files:
860
- - lang: typescript
1400
+ - name: kubb.config.ts
1401
+ lang: typescript
1402
+ twoslash: false
861
1403
  code: |
1404
+ import { defineConfig } from 'kubb'
862
1405
  import { pluginTs } from '@kubb/plugin-ts'
863
1406
 
864
- pluginTs({
865
- transformer: {
866
- schema(node) {
867
- return { ...node, description: undefined }
868
- },
869
- },
1407
+ export default defineConfig({
1408
+ input: { path: './petStore.yaml' },
1409
+ output: { path: './src/gen' },
1410
+ plugins: [
1411
+ pluginTs({
1412
+ transformer: {
1413
+ schema(node) {
1414
+ return { ...node, description: undefined }
1415
+ },
1416
+ },
1417
+ }),
1418
+ ],
870
1419
  })
871
- twoslash: false
872
1420
  - name: Prefix every operationId
873
1421
  files:
874
- - lang: typescript
1422
+ - name: kubb.config.ts
1423
+ lang: typescript
1424
+ twoslash: false
875
1425
  code: |
1426
+ import { defineConfig } from 'kubb'
876
1427
  import { pluginTs } from '@kubb/plugin-ts'
877
1428
 
878
- pluginTs({
879
- transformer: {
880
- operation(node) {
881
- return { ...node, operationId: `api_${node.operationId}` }
882
- },
883
- },
1429
+ export default defineConfig({
1430
+ input: { path: './petStore.yaml' },
1431
+ output: { path: './src/gen' },
1432
+ plugins: [
1433
+ pluginTs({
1434
+ transformer: {
1435
+ operation(node) {
1436
+ return { ...node, operationId: `api_${node.operationId}` }
1437
+ },
1438
+ },
1439
+ }),
1440
+ ],
884
1441
  })
885
- twoslash: false
886
1442
  tip: |
887
- Use `transformer` to rewrite node properties before printing. For output naming customization, use `resolver` instead.
1443
+ Use `transformer` to rewrite node properties before printing. For changing the names of generated symbols and files, use `resolver` instead.
888
1444
  notes:
889
1445
  - type: tip
890
1446
  body: |
891
- See [Tanstack Query](https://tanstack.com/query) for more information about React Query.
1447
+ Reference: [TanStack Query](https://tanstack.com/query) for cache, retries, and devtools.
892
1448
  examples:
893
1449
  - name: kubb.config.ts
894
1450
  files:
895
1451
  - lang: typescript
1452
+ twoslash: false
896
1453
  code: |
897
1454
  import { defineConfig } from 'kubb'
898
1455
  import { pluginTs } from '@kubb/plugin-ts'
899
1456
  import { pluginReactQuery } from '@kubb/plugin-react-query'
900
1457
 
901
1458
  export default defineConfig({
902
- input: {
903
- path: './petStore.yaml',
904
- },
905
- output: {
906
- path: './src/gen',
907
- },
1459
+ input: { path: './petStore.yaml' },
1460
+ output: { path: './src/gen' },
908
1461
  plugins: [
909
1462
  pluginTs(),
910
1463
  pluginReactQuery({
911
- output: {
912
- path: './hooks',
913
- },
1464
+ output: { path: './hooks' },
914
1465
  group: {
915
1466
  type: 'tag',
916
1467
  name: ({ group }) => `${group}Hooks`,
917
1468
  },
918
- client: {
919
- dataReturnType: 'full',
920
- },
921
- mutation: {
922
- methods: ['post', 'put', 'delete'],
923
- },
1469
+ client: { dataReturnType: 'full' },
1470
+ mutation: { methods: ['post', 'put', 'delete'] },
924
1471
  infinite: {
925
1472
  queryParam: 'next_page',
926
1473
  initialPageParam: 0,
@@ -935,4 +1482,3 @@ examples:
935
1482
  }),
936
1483
  ],
937
1484
  })
938
- twoslash: false