@kubb/plugin-react-query 5.10.3 → 5.10.4

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.
package/dist/index.cjs CHANGED
@@ -33,11 +33,11 @@ let node_fs = require("node:fs");
33
33
  node_fs = __toESM(node_fs, 1);
34
34
  // kubb-extension.json
35
35
  var compatibility = {
36
- "kubb": ">=5.18.4",
36
+ "kubb": ">=5.19.0",
37
37
  "node": ">=22.22"
38
38
  };
39
39
  // package.json
40
- var version = "5.10.3";
40
+ var version = "5.10.4";
41
41
  // ../../internals/client/src/resolveClient.ts
42
42
  /**
43
43
  * Resolves which client plugin a consumer (react-query, vue-query, swr, mcp) should call for an
package/dist/index.js CHANGED
@@ -6,11 +6,11 @@ import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
6
6
  import fs from "node:fs";
7
7
  // kubb-extension.json
8
8
  var compatibility = {
9
- "kubb": ">=5.18.4",
9
+ "kubb": ">=5.19.0",
10
10
  "node": ">=22.22"
11
11
  };
12
12
  // package.json
13
- var version = "5.10.3";
13
+ var version = "5.10.4";
14
14
  // ../../internals/client/src/resolveClient.ts
15
15
  /**
16
16
  * Resolves which client plugin a consumer (react-query, vue-query, swr, mcp) should call for an
@@ -1,62 +1,49 @@
1
1
  # Call operations
2
2
 
3
- `@kubb/plugin-react-query` can generate a typed TanStack Query hook for each operation. `GET` operations get query hooks such as `useGetPetById`, and `POST`, `PUT`, `PATCH`, and `DELETE` operations get mutation hooks such as `useDeletePet`. Each hook calls the client function from `@kubb/plugin-fetch`, `@kubb/plugin-axios`, or `@kubb/plugin-client`.
3
+ Use the generated TanStack Query hooks and factories in React: queries, mutations, infinite lists, and suspense.
4
4
 
5
- > [!IMPORTANT]
6
- > By default the plugin emits only the factory helpers (`queryOptions`, `mutationOptions`, `queryKey`, `mutationKey`). Set [`hooks: true`](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options#hooks) in the plugin options to also generate the `use*` hooks shown below.
5
+ ## What each plugin generates
7
6
 
8
- ## Queries
7
+ Each hook and its helpers live in a file named after the hook, such as `src/gen/hooks/useGetPetById.ts`.
9
8
 
10
- A query hook takes the operation's grouped request config (`path`, `query`, `headers`, whichever the operation declares) as its first argument and returns a TanStack `UseQueryResult`:
9
+ | Operation | Exports |
10
+ | ------------------------------------------- | --------------------------------------------------------------------------------------------------- |
11
+ | Query (`GET`, `QUERY`) | `getPetByIdQueryKey`, `getPetByIdQueryOptions`, plus `useGetPetById` with `hooks` |
12
+ | Mutation (`POST`, `PUT`, `PATCH`, `DELETE`) | `updatePetMutationKey`, `updatePetMutationOptions`, plus `useUpdatePet` with `hooks` |
13
+ | Infinite, with `infinite` and `hooks` | `findPetsByTagsInfiniteQueryKey`, `findPetsByTagsInfiniteQueryOptions`, `useFindPetsByTagsInfinite` |
14
+ | Suspense, with `suspense` and `hooks` | `useGetPetByIdSuspense`, plus `useFindPetsByTagsSuspenseInfinite` with `infinite` |
15
+ | Server-Sent Events or JSON Lines | Nothing. Call the client function directly. |
11
16
 
12
- ```typescript
13
- import { useGetPetById } from './gen/hooks/useGetPetById'
17
+ ## Call a hook
14
18
 
15
- const { data, error, isLoading } = useGetPetById({ path: { petId: 1 } })
16
- ```
19
+ The examples set `hooks: true`, and [`integerType: 'number'`](https://kubb.dev/extensions/kubb-labs/adapter-oas/options#integertype) on the adapter so `petId` is a `number`. Path keys keep their spec names unless you set [`pathParamsCasing`](https://kubb.dev/extensions/kubb-labs/adapter-oas/options#pathparamscasing).
17
20
 
18
- The examples set [`integerType: 'number'`](https://kubb.dev/extensions/kubb-labs/adapter-oas/options#integertype) on the adapter, so an `int64` id such as `petId` is a `number`. With the default it is a `bigint` and the call takes `1n`.
21
+ ::tabs
19
22
 
20
- Path keys keep the names from the spec, so a `{pet-id}` placeholder is passed as `path: { 'pet-id': 1 }`. Set [`pathParamsCasing: 'camelcase'`](https://kubb.dev/extensions/kubb-labs/adapter-oas/options#pathparamscasing) on the adapter (`@kubb/adapter-oas` 5.20.0 or later) to pass `path: { petId: 1 }` instead, the name MSW handlers and query keys already use. The HTTP request stays the same.
23
+ :::tabs-item{label="Query"}
21
24
 
22
- The second argument has two optional groups. `query` takes any TanStack Query option, and `query.client` sets the `QueryClient` to use. The top-level `client` takes request config for this call, such as `baseURL` or `signal`:
25
+ The first argument takes the request groups: `path`, `query`, `headers`. The second takes TanStack Query options in `query`, with `query.client` for the `QueryClient`, and request config such as `baseURL` or `signal` in `client`.
23
26
 
24
27
  ```typescript
25
28
  import { useGetPetById } from './gen/hooks/useGetPetById'
26
29
 
27
- useGetPetById(
30
+ const { data, error, isLoading } = useGetPetById(
28
31
  { path: { petId: 1 } },
29
- {
30
- query: { staleTime: 60_000 },
31
- client: { baseURL: 'https://api.example.com/v1' },
32
- },
32
+ { query: { staleTime: 60_000 }, client: { baseURL: 'https://api.example.com/v1' } },
33
33
  )
34
34
  ```
35
35
 
36
- ## Factories
37
-
38
- Each query operation also exports a `queryOptions` factory and a `queryKey` helper. Use them to prefetch or invalidate a query outside a hook. The key includes the operation URL and path parameters, followed by query or body values when present:
39
-
40
- ```typescript
41
- import { useQueryClient } from '@tanstack/react-query'
42
- import { getPetByIdQueryKey, getPetByIdQueryOptions } from './gen/hooks/useGetPetById'
43
-
44
- const queryClient = useQueryClient()
45
-
46
- await queryClient.prefetchQuery(getPetByIdQueryOptions({ path: { petId: 1 } }))
47
- queryClient.invalidateQueries({ queryKey: getPetByIdQueryKey({ path: { petId: 1 } }) })
48
- ```
36
+ :::
49
37
 
50
- ## Mutations
38
+ :::tabs-item{label="Mutation"}
51
39
 
52
- A mutation hook takes only an options object. The grouped request config is the mutation variable, so you pass it to `mutate` or `mutateAsync`:
40
+ A mutation hook takes only the options object, with `mutation` and `client`. Pass the request groups to `mutate` or `mutateAsync`.
53
41
 
54
42
  ```typescript
55
43
  import { useQueryClient } from '@tanstack/react-query'
56
44
  import { useDeletePet } from './gen/hooks/useDeletePet'
57
45
 
58
46
  const queryClient = useQueryClient()
59
-
60
47
  const { mutate } = useDeletePet({
61
48
  mutation: { onSuccess: () => queryClient.invalidateQueries() },
62
49
  })
@@ -64,108 +51,57 @@ const { mutate } = useDeletePet({
64
51
  mutate({ path: { petId: 1 } })
65
52
  ```
66
53
 
67
- Each mutation also exports a `mutationOptions` factory and a `mutationKey` helper.
68
-
69
- ## Errors and transport
70
-
71
- Each generated hook calls the client function with `throwOnError: true`. A failed request sets TanStack Query's `error`, typed from the error responses in the spec. Set the base URL, authentication, interceptors, serialization, and validation on the client plugin. See the [plugin-fetch](https://kubb.dev/extensions/kubb-labs/plugin-fetch) or [plugin-axios](https://kubb.dev/extensions/kubb-labs/plugin-axios) guide. With [plugin-client](https://kubb.dev/extensions/kubb-labs/plugin-client), set these in your own client module.
72
-
73
- ## Customize cache keys
74
-
75
- Set `queryKey` on the plugin to change generated keys. String entries are emitted as source code, so use `JSON.stringify` for a string literal.
76
-
77
- ```typescript [kubb.config.ts]
78
- import { pluginReactQuery } from '@kubb/plugin-react-query'
79
-
80
- pluginReactQuery({
81
- queryKey: ({ node }) => [JSON.stringify(node.operationId)],
82
- })
83
- ```
84
-
85
- > [!WARNING]
86
- > This `queryKey` gives every call the same key, such as `['getUserByName']`. If path or query values select different data, add them to the key.
87
-
88
- ## Load more pages
54
+ :::
89
55
 
90
- Configure `infinite` with a query parameter declared by the operation, its initial value, and the response path for the next cursor. Infinite output needs `hooks: true`, and only operations that declare the `queryParam` receive it.
56
+ :::tabs-item{label="Infinite"}
91
57
 
92
- ::code-group
58
+ Only operations that declare `infinite.queryParam` get an infinite hook. Point `nextParam` at the cursor in the response, and give `initialPageParam` the type of the query parameter. See [Infinite queries](https://kubb.dev/extensions/kubb-labs/plugin-react-query/guides/infinite-queries).
93
59
 
94
60
  ```typescript [kubb.config.ts]
95
- import { pluginReactQuery } from '@kubb/plugin-react-query'
96
-
97
61
  pluginReactQuery({
98
62
  hooks: true,
99
- infinite: {
100
- queryParam: 'page',
101
- initialPageParam: 0,
102
- nextParam: 'pagination.next.cursor',
103
- },
63
+ infinite: { queryParam: 'page', initialPageParam: 0, nextParam: 'pagination.next.cursor' },
104
64
  })
105
65
  ```
106
66
 
107
- ```typescript [usage.ts]
108
- import { useInfiniteQuery } from '@tanstack/react-query'
109
- import { findPetsByTagsInfiniteQueryOptions } from './gen/hooks/useFindPetsByTagsInfinite'
67
+ ```typescript
68
+ import { useFindPetsByTagsInfinite } from './gen/hooks/useFindPetsByTagsInfinite'
110
69
 
111
- const { data, fetchNextPage, hasNextPage } = useInfiniteQuery(
112
- findPetsByTagsInfiniteQueryOptions({ query: { tags: ['dog'] } }),
113
- )
70
+ const { data, fetchNextPage, hasNextPage } = useFindPetsByTagsInfinite({ query: { tags: ['dog'] } })
114
71
  ```
115
72
 
116
- ::
117
-
118
- `nextParam` takes dot notation or an array of keys. Change it to match your API's response, and give `initialPageParam` the same type as the query parameter. Without `getNextPageParam`, `nextParam` or `cursorParam`, the generated options increment the page number until a page returns an empty array.
119
-
120
- ## Use suspense
73
+ :::
121
74
 
122
- Set `suspense: {}` and `hooks: true` to generate suspense hooks alongside regular hooks. Suspense requires TanStack Query v5 or higher.
123
-
124
- ::code-group
125
-
126
- ```typescript [kubb.config.ts]
127
- import { pluginReactQuery } from '@kubb/plugin-react-query'
75
+ :::tabs-item{label="Suspense"}
128
76
 
129
- pluginReactQuery({ hooks: true, suspense: {} })
130
- ```
77
+ Set `suspense: {}` next to `hooks: true`. A suspense hook takes the same arguments as the query hook.
131
78
 
132
- ```typescript [usage.ts]
79
+ ```typescript
133
80
  import { useGetPetByIdSuspense } from './gen/hooks/useGetPetByIdSuspense'
134
81
 
135
82
  const { data } = useGetPetByIdSuspense({ path: { petId: 1 } })
136
83
  ```
137
84
 
85
+ :::
86
+
138
87
  ::
139
88
 
140
- ## Share hook options
89
+ ## Prefetch and invalidate
141
90
 
142
- Set [`customOptions`](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options#customoptions) to call one options function from every generated hook.
91
+ The factories work without `hooks`, so you can prefetch or invalidate outside a component:
143
92
 
144
- ```typescript [kubb.config.ts]
145
- import { pluginReactQuery } from '@kubb/plugin-react-query'
93
+ ```typescript
94
+ import { getPetByIdQueryKey, getPetByIdQueryOptions } from './gen/hooks/useGetPetById'
95
+ import { queryClient } from './queryClient' // the QueryClient you pass to QueryClientProvider
146
96
 
147
- pluginReactQuery({
148
- hooks: true,
149
- customOptions: {
150
- importPath: './useCustomHookOptions',
151
- name: 'useCustomHookOptions',
152
- },
153
- })
97
+ await queryClient.prefetchQuery(getPetByIdQueryOptions({ path: { petId: 1 } }))
98
+ queryClient.invalidateQueries({ queryKey: getPetByIdQueryKey({ path: { petId: 1 } }) })
154
99
  ```
155
100
 
156
- Create the function at `importPath`, resolved from the generated hooks folder (here `src/gen/hooks/useCustomHookOptions.ts`). Each hook passes `{ hookName, operationId }`. The generated barrel re-exports `HookOptions`.
101
+ [TanStack Query keys](https://kubb.dev/extensions/kubb-labs/plugin-react-query/guides/query-keys) shows the default keys. To change them, set [`queryKey`](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options#querykey) and [`mutationKey`](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options#mutationkey). To share options across hooks, set [`customOptions`](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options#customoptions).
157
102
 
158
- ```typescript [src/gen/hooks/useCustomHookOptions.ts]
159
- import type { HookOptions } from './HookOptions'
103
+ ## Handle errors
160
104
 
161
- export function useCustomHookOptions(
162
- { hookName }: { hookName: keyof HookOptions; operationId: string },
163
- ) {
164
- if (hookName === 'useGetPetById') {
165
- return { staleTime: 60_000 } satisfies HookOptions['useGetPetById']
166
- }
167
- return {}
168
- }
169
- ```
105
+ Each hook calls the client with `throwOnError: true`, so a failed request sets TanStack Query's `error`, typed from the spec's error responses.
170
106
 
171
- Per-call query options override shared options.
107
+ Next, set the base URL, auth, and interceptors on your client plugin: [plugin-fetch](https://kubb.dev/extensions/kubb-labs/plugin-fetch), [plugin-axios](https://kubb.dev/extensions/kubb-labs/plugin-axios), or your own module with [plugin-client](https://kubb.dev/extensions/kubb-labs/plugin-client/guides/write-your-client).
@@ -0,0 +1,86 @@
1
+ # Infinite queries with useInfiniteQuery
2
+
3
+ Generate `useInfiniteQuery` hooks for paginated endpoints, read the next cursor from the response, and stop once the last page arrives.
4
+
5
+ ## Generate infinite hooks
6
+
7
+ When an endpoint pages through results with a query parameter, set [`infinite`](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options#infinite) next to `hooks: true`. Only operations that declare `infinite.queryParam` get an infinite hook, and the hook sends each page param as that parameter:
8
+
9
+ ::code-group
10
+
11
+ ```typescript [kubb.config.ts]
12
+ import { defineConfig } from 'kubb/config'
13
+ import { pluginTs } from '@kubb/plugin-ts'
14
+ import { pluginFetch } from '@kubb/plugin-fetch'
15
+ import { pluginReactQuery } from '@kubb/plugin-react-query'
16
+
17
+ export default defineConfig({
18
+ input: './petStore.yaml',
19
+ output: { path: './src/gen' },
20
+ plugins: [
21
+ pluginTs(),
22
+ pluginFetch(),
23
+ pluginReactQuery({
24
+ hooks: true,
25
+ infinite: { queryParam: 'page', initialPageParam: 0, nextParam: 'pagination.next.cursor' },
26
+ }),
27
+ ],
28
+ })
29
+ ```
30
+
31
+ ```typescript [Generated]
32
+ // src/gen/hooks/useFindPetsByTagsInfinite.ts, types left out
33
+ export const findPetsByTagsInfiniteQueryKey = ({ query }: Omit<FindPetsByTagsOptions, 'headers'> = {}) =>
34
+ [{ url: '/pet/findByTags', infinite: true }, ...(query ? [query] : [])] as const
35
+
36
+ export function findPetsByTagsInfiniteQueryOptions({ query }: FindPetsByTagsOptions = {}, config = {}) {
37
+ const queryKey = findPetsByTagsInfiniteQueryKey({ query })
38
+ return infiniteQueryOptions({
39
+ queryKey,
40
+ queryFn: async ({ signal, pageParam }) => {
41
+ return findPetsByTags({ ...config, query: { ...query, page: pageParam }, signal: config.signal ?? signal, throwOnError: true }).unwrap()
42
+ },
43
+ initialPageParam: 0,
44
+ getNextPageParam: (lastPage) => lastPage?.['pagination']?.['next']?.['cursor'],
45
+ })
46
+ }
47
+ ```
48
+
49
+ ```typescript [Usage]
50
+ import { useFindPetsByTagsInfinite } from './gen/hooks/useFindPetsByTagsInfinite'
51
+
52
+ const { data, fetchNextPage, hasNextPage } = useFindPetsByTagsInfinite({ query: { tags: ['dog'] } })
53
+ ```
54
+
55
+ ::
56
+
57
+ - Give `initialPageParam` the type of the query parameter.
58
+ - The infinite key adds `infinite: true`, so it never shares a cache entry with the plain query. See [TanStack Query keys](https://kubb.dev/extensions/kubb-labs/plugin-react-query/guides/query-keys).
59
+ - Set [`suspense: {}`](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options#suspense) as well to get `useFindPetsByTagsSuspenseInfinite`.
60
+
61
+ ## Read the next page from the response
62
+
63
+ The generated `getNextPageParam` comes from the first of these options you set:
64
+
65
+ | Option | Generated `getNextPageParam` |
66
+ | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
67
+ | [`getNextPageParam`](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options#infinitegetnextpageparam) | Your function's source, copied as written. It cannot use variables from the config file. |
68
+ | [`nextParam`](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options#infinitenextparam) | Reads that path on the last page, such as `lastPage?.['pagination']?.['next']?.['cursor']`. |
69
+ | [`cursorParam`](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options#infinitecursorparam) | Deprecated. Reads that top-level field. |
70
+ | None | Counts pages up from `initialPageParam` until a page is an empty array. |
71
+
72
+ `getPreviousPageParam`, `previousParam`, and `cursorParam` set the previous page in the same order. With [`returnType: 'full'`](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options#returntype), the `nextParam` and `cursorParam` accessors read from `lastPage.data`.
73
+
74
+ ## hasNextPage never turns false
75
+
76
+ Generation prints this warning for the operation:
77
+
78
+ ```text
79
+ findPetsByTags: the default infinite page params expect an array response and never end for this one. Set `infinite.getNextPageParam` (or `nextParam`) to read the next page from the response.
80
+ ```
81
+
82
+ Without `getNextPageParam`, `nextParam`, or `cursorParam`, the hook counts pages up from `initialPageParam` until a page is an empty array. That works only for array responses, so generation warns for any other response. It skips the warning when `previousParam` or `getPreviousPageParam` is set, though the count still never ends.
83
+
84
+ To fix it, set `nextParam` to the cursor path, or `getNextPageParam` when you must compute the next page.
85
+
86
+ Next, see every [`infinite` option](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options#infinite), including `definePageParam` for a typed `getNextPageParam`.
@@ -0,0 +1,104 @@
1
+ # TanStack Query keys
2
+
3
+ See the query key each generated React Query hook uses, invalidate or read the cache with it, and add your own prefix to the default key.
4
+
5
+ ## The default query key
6
+
7
+ When you call `invalidateQueries`, `getQueryData`, or `setQueryData`, take the key from the `<operation>QueryKey` helper in each hook file. It builds `[{ url, params }, query, body]`:
8
+
9
+ ::code-group
10
+
11
+ ```typescript [Generated]
12
+ // src/gen/hooks/useGetPetById.ts
13
+ export const getPetByIdQueryKey = ({ path }: Omit<GetPetByIdOptions, 'headers'>) => [{ url: '/pet/:petId', params: path }] as const
14
+
15
+ // src/gen/hooks/useFindPetsByTags.ts
16
+ export const findPetsByTagsQueryKey = ({ query }: Omit<FindPetsByTagsOptions, 'headers'> = {}) =>
17
+ [{ url: '/pet/findByTags' }, ...(query ? [query] : [])] as const
18
+ ```
19
+
20
+ ```typescript [Usage]
21
+ import { getPetByIdQueryKey } from './gen/hooks/useGetPetById'
22
+ import { findPetsByTagsQueryKey } from './gen/hooks/useFindPetsByTags'
23
+ import { queryClient } from './queryClient'
24
+
25
+ getPetByIdQueryKey({ path: { petId: 1 } }) // [{ url: '/pet/:petId', params: { petId: 1 } }]
26
+ findPetsByTagsQueryKey({ query: { tags: ['dog'] } }) // [{ url: '/pet/findByTags' }, { tags: ['dog'] }]
27
+
28
+ const pet = queryClient.getQueryData(getPetByIdQueryKey({ path: { petId: 1 } }))
29
+ ```
30
+
31
+ ::
32
+
33
+ - `url` is the spec path with `:name` placeholders, such as `'/pet/:petId'`.
34
+ - `params` holds the path parameters, when the path has any.
35
+ - `query` and `body` follow when the operation has them and the call passes them.
36
+ - Headers and cookies stay out of the key.
37
+ - Infinite variants add `infinite: true` to the first entry, so they never share a cache entry with the plain query.
38
+ - Each hook returns the key it used as `queryKey` on its result.
39
+
40
+ ## Invalidate every call of an operation
41
+
42
+ TanStack Query matches a key by its prefix, and an object entry by the fields you pass. A key built without `query` matches every cached call of that operation with the same path parameters, infinite ones included:
43
+
44
+ ```typescript
45
+ queryClient.invalidateQueries({ queryKey: findPetsByTagsQueryKey() })
46
+ ```
47
+
48
+ ## Mutation keys
49
+
50
+ Each mutation gets a `<operation>MutationKey` helper that returns `[{ url }]`, for filters such as `useIsMutating` or `useMutationState`:
51
+
52
+ ::code-group
53
+
54
+ ```typescript [Generated]
55
+ // src/gen/hooks/useUpdatePet.ts
56
+ export const updatePetMutationKey = () => [{ url: '/pet' }] as const
57
+ ```
58
+
59
+ ```typescript [Usage]
60
+ import { useIsMutating } from '@tanstack/react-query'
61
+ import { updatePetMutationKey } from './gen/hooks/useUpdatePet'
62
+
63
+ const saving = useIsMutating({ mutationKey: updatePetMutationKey() })
64
+ ```
65
+
66
+ ::
67
+
68
+ ## Extend the default key
69
+
70
+ When you need your own entry, such as an API version, set [`queryKey`](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options#querykey) and spread `queryKeyTransformer`. Do not read single entries of its result: they can change between releases.
71
+
72
+ > [!WARNING]
73
+ > Returned strings are copied into the generated code as written. Wrap a string literal in `JSON.stringify(...)`.
74
+
75
+ ::code-group
76
+
77
+ ```typescript [kubb.config.ts]
78
+ import { defineConfig } from 'kubb/config'
79
+ import { pluginTs } from '@kubb/plugin-ts'
80
+ import { pluginFetch } from '@kubb/plugin-fetch'
81
+ import { pluginReactQuery, queryKeyTransformer } from '@kubb/plugin-react-query'
82
+
83
+ export default defineConfig({
84
+ input: './petStore.yaml',
85
+ output: { path: './src/gen' },
86
+ plugins: [
87
+ pluginTs(),
88
+ pluginFetch(),
89
+ pluginReactQuery({
90
+ queryKey: (props) => [JSON.stringify('v2'), ...queryKeyTransformer(props)],
91
+ }),
92
+ ],
93
+ })
94
+ ```
95
+
96
+ ```typescript [Generated]
97
+ export const getPetByIdQueryKey = ({ path }: Omit<GetPetByIdOptions, 'headers'>) => ["v2", { url: '/pet/:petId', params: path }] as const
98
+ ```
99
+
100
+ ::
101
+
102
+ The builder also gets the `variant`, so it can return a different key for suspense or infinite hooks. [`mutationKey`](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options#mutationkey) works the same way with `mutationKeyTransformer`.
103
+
104
+ Next, [generate infinite queries](https://kubb.dev/extensions/kubb-labs/plugin-react-query/guides/infinite-queries) for paginated endpoints.
package/docs/options.md CHANGED
@@ -1,34 +1,47 @@
1
1
  # Options
2
2
 
3
- Pass these options to `pluginReactQuery()`. Shared options link to [Shared plugin options](https://kubb.dev/docs/5.x/reference/plugin-options) for details.
3
+ Pass these options to `pluginReactQuery()`. Shared options link to [Shared plugin options](https://kubb.dev/docs/5.x/reference/plugin-options).
4
4
 
5
5
  ## Options overview
6
6
 
7
- | Option | Purpose | Default |
8
- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
9
- | [`output`](https://kubb.dev/docs/5.x/reference/plugin-options#output) | Where files are written: `path`, `mode`, `barrel`, `banner`, `footer`. | `{ path: 'hooks', barrel: { type: 'named' } }` |
10
- | [`group`](https://kubb.dev/docs/5.x/reference/plugin-options#group) | Split output by tag or path: `type`, `name`. | None |
11
- | [`client`](#client) | Which registered client plugin the hooks call. | Auto-detected |
12
- | [`returnType`](#returntype) | Shape of the value the hooks resolve to. | `'data'` |
13
- | [`infinite`](#infinite) | Generate `useInfiniteQuery` hooks for pagination. | `false` |
14
- | [`suspense`](#suspense) | Generate `useSuspenseQuery` hooks. | `false` |
15
- | [`query`](#query) | Configure the query hooks. | `{ methods: ['GET', 'QUERY'], importPath: '@tanstack/react-query' }` |
16
- | [`queryKey`](#querykey) | Build the `queryKey` for each query hook. | Exported `queryKeyTransformer` |
17
- | [`mutation`](#mutation) | Configure the mutation hooks. | `{ methods: ['POST', 'PUT', 'PATCH', 'DELETE'], importPath: '@tanstack/react-query' }` |
18
- | [`mutationKey`](#mutationkey) | Build the `mutationKey` for each mutation hook. | Exported `mutationKeyTransformer` |
19
- | [`customOptions`](#customoptions) | Route every hook through a custom options function. | None |
20
- | [`hooks`](#hooks) | Emit `use*` hook functions on top of the factories. | `false` |
21
- | [`include`](https://kubb.dev/docs/5.x/reference/plugin-options#include) | Keep only operations that match. | None |
22
- | [`exclude`](https://kubb.dev/docs/5.x/reference/plugin-options#exclude) | Skip operations that match. | `[]` |
23
- | [`override`](https://kubb.dev/docs/5.x/reference/plugin-options#override) | Apply different options per pattern. | `[]` |
24
- | [`resolver`](#resolver) | Customize generated names and file paths. | `resolverReactQuery` |
25
- | [`macros`](https://kubb.dev/docs/5.x/reference/plugin-options#macros) | Rewrite AST nodes before printing. | `[]` |
7
+ | Option | Purpose | Default |
8
+ | ------------------------------------------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------- |
9
+ | [`client`](#client) | Client plugin the hooks call. | Auto-detected |
10
+ | [`returnType`](#returntype) | What the hooks resolve to. | `'data'` |
11
+ | [`hooks`](#hooks) | Emit `use*` hooks on top of the factories. | `false` |
12
+ | [`query`](#query) | Which methods become queries, and the TanStack import. | `{ methods: ['GET', 'QUERY'], importPath: '@tanstack/react-query' }` |
13
+ | [`query.methods`](#querymethods) | Methods that become queries. | `['GET', 'QUERY']` |
14
+ | [`query.importPath`](#queryimportpath) | Import path of the query helpers. | `'@tanstack/react-query'` |
15
+ | [`queryKey`](#querykey) | Build the `queryKey` of each query. | `queryKeyTransformer` |
16
+ | [`mutation`](#mutation) | Which methods become mutations, and the TanStack import. | `{ methods: ['POST', 'PUT', 'PATCH', 'DELETE'], importPath: '@tanstack/react-query' }` |
17
+ | [`mutation.methods`](#mutationmethods) | Methods that become mutations. | `['POST', 'PUT', 'PATCH', 'DELETE']` |
18
+ | [`mutation.importPath`](#mutationimportpath) | Import path of the mutation helpers. | `'@tanstack/react-query'` |
19
+ | [`mutationKey`](#mutationkey) | Build the `mutationKey` of each mutation. | `mutationKeyTransformer` |
20
+ | [`infinite`](#infinite) | Generate infinite queries for pagination. | `false` |
21
+ | [`infinite.queryParam`](#infinitequeryparam) | Query parameter for the page cursor. | `'id'` |
22
+ | [`infinite.initialPageParam`](#infiniteinitialpageparam) | First `pageParam`. | `0` |
23
+ | [`infinite.nextParam`](#infinitenextparam) | Path to the next cursor. | `null` |
24
+ | [`infinite.previousParam`](#infinitepreviousparam) | Path to the previous cursor. | `null` |
25
+ | [`infinite.getNextPageParam`](#infinitegetnextpageparam) | Compute the next page param. | `null` |
26
+ | [`infinite.getPreviousPageParam`](#infinitegetpreviouspageparam) | Compute the previous page param. | `null` |
27
+ | [`infinite.cursorParam`](#infinitecursorparam) | Deprecated cursor field. | `null` |
28
+ | [`suspense`](#suspense) | Generate `useSuspenseQuery` hooks. | `false` |
29
+ | [`customOptions`](#customoptions) | Route every hook through your own options function. | None |
30
+ | [`customOptions.importPath`](#customoptionsimportpath) | Module of your function. | None, required |
31
+ | [`customOptions.name`](#customoptionsname) | Name of your function. | `'useCustomHookOptions'` |
32
+ | [`output`](https://kubb.dev/docs/5.x/reference/plugin-options#output) | Where files are written. | `{ path: 'hooks', barrel: { type: 'named' } }` |
33
+ | [`group`](https://kubb.dev/docs/5.x/reference/plugin-options#group) | Split output by tag or path. | None |
34
+ | [`include`](https://kubb.dev/docs/5.x/reference/plugin-options#include) | Keep only operations that match. | None |
35
+ | [`exclude`](https://kubb.dev/docs/5.x/reference/plugin-options#exclude) | Skip operations that match. | `[]` |
36
+ | [`override`](https://kubb.dev/docs/5.x/reference/plugin-options#override) | Apply different options per pattern. | `[]` |
37
+ | [`resolver`](#resolver) | Customize generated names and file paths. | `resolverReactQuery` |
38
+ | [`macros`](https://kubb.dev/docs/5.x/reference/plugin-options#macros) | Rewrite AST nodes before printing. | `[]` |
26
39
 
27
40
  ## Option details
28
41
 
29
42
  ### client
30
43
 
31
- Selects which registered client plugin the hooks call. A single registered client is auto-detected. Set `client` when several are registered.
44
+ The client plugin the hooks call. Kubb detects a lone client plugin, so set `client` only when several are registered. Generation fails when none is.
32
45
 
33
46
  | | |
34
47
  | -------- | -------------------------------- |
@@ -38,171 +51,62 @@ Selects which registered client plugin the hooks call. A single registered clien
38
51
  ::field-group
39
52
 
40
53
  :::field{name="'fetch'"}
41
- Calls the operations generated by [`@kubb/plugin-fetch`](https://kubb.dev/extensions/kubb-labs/plugin-fetch).
54
+ Calls [`@kubb/plugin-fetch`](https://kubb.dev/extensions/kubb-labs/plugin-fetch).
42
55
  :::
43
56
 
44
57
  :::field{name="'axios'"}
45
- Calls the operations generated by [`@kubb/plugin-axios`](https://kubb.dev/extensions/kubb-labs/plugin-axios).
58
+ Calls [`@kubb/plugin-axios`](https://kubb.dev/extensions/kubb-labs/plugin-axios).
46
59
  :::
47
60
 
48
61
  :::field{name="'client'"}
49
- Calls the operations generated by [`@kubb/plugin-client`](https://kubb.dev/extensions/kubb-labs/plugin-client). The client module must also export the `RequestConfig` and `ResponseErrorConfig` types.
62
+ Calls [`@kubb/plugin-client`](https://kubb.dev/extensions/kubb-labs/plugin-client). Your client module must also export `RequestConfig` and `ResponseErrorConfig`.
50
63
  :::
51
64
 
52
65
  ::
53
66
 
54
67
  ### returnType
55
68
 
56
- Shape of the value the generated hooks resolve to.
69
+ What the hooks resolve to.
57
70
 
58
71
  | | |
59
72
  | -------- | ------------------ |
60
- | Type | `'full' \| 'data'` |
73
+ | Type | `'data' \| 'full'` |
61
74
  | Required | `false` |
62
75
  | Default | `'data'` |
63
76
 
64
77
  ::field-group
65
78
 
66
- :::field{name="'full'"}
67
- Returns the client's whole `RequestResult` (`{ status, data, error, contentType, headers, request, response }`), so a hook can read the status and the response headers next to `data`. Infinite queries read the page cursor from `lastPage.data`. Needs a client plugin whose own `returnType` is `'full'` or `'plain'`. With a client `returnType: 'data'`, generation stops with an error.
79
+ :::field{name="'data'"}
80
+ Resolves to the success body.
68
81
  :::
69
82
 
70
- :::field{name="'data'"}
71
- Returns the success body.
83
+ :::field{name="'full'"}
84
+ Resolves to the client's whole `RequestResult`, so a hook can read the status and headers. Needs a client plugin with `returnType` `'full'` or `'plain'`, or generation fails.
72
85
  :::
73
86
 
74
87
  ::
75
88
 
76
- ```ts
89
+ ```typescript
77
90
  pluginReactQuery({ returnType: 'full' })
78
91
 
92
+ // with integerType: 'number' on the adapter
79
93
  const { data: result } = useGetPetById({ path: { petId: 1 } })
80
-
81
94
  const etag = result?.response.headers.get('ETag')
82
- const pet = result?.data
83
95
  ```
84
96
 
85
- The examples set [`integerType: 'number'`](https://kubb.dev/extensions/kubb-labs/adapter-oas/options#integertype) on the adapter, so an `int64` id such as `petId` is a `number`. With the default it is a `bigint` and the call takes `1n`.
86
-
87
- ### infinite
88
-
89
- Adds `infiniteQueryOptions` and `useInfiniteQuery` for operations that declare the query parameter named in `infinite.queryParam`. Needs [`hooks: true`](#hooks).
90
-
91
- | | |
92
- | -------- | ---------------------------- |
93
- | Type | `Partial<Infinite> \| false` |
94
- | Required | `false` |
95
- | Default | `false` |
96
-
97
- #### infinite.queryParam
98
-
99
- Query parameter that carries the page cursor.
100
-
101
- | | |
102
- | -------- | -------- |
103
- | Type | `string` |
104
- | Required | `false` |
105
- | Default | `'id'` |
106
-
107
- #### infinite.initialPageParam
97
+ ### hooks
108
98
 
109
- Initial value for `pageParam` on the first fetch.
99
+ Set `true` to generate `useQuery` and `useMutation` hooks on top of the `queryOptions`, `mutationOptions`, and key helpers. [Call operations](https://kubb.dev/extensions/kubb-labs/plugin-react-query/guides/calling-operations#what-each-plugin-generates) lists every export.
110
100
 
111
101
  | | |
112
102
  | -------- | --------- |
113
- | Type | `unknown` |
103
+ | Type | `boolean` |
114
104
  | Required | `false` |
115
- | Default | `0` |
116
-
117
- #### infinite.nextParam
118
-
119
- Path to the next-page cursor on the response, as dot notation (`'pagination.next.id'`) or array form (`['pagination', 'next', 'id']`).
120
-
121
- | | |
122
- | -------- | --------------------------------- |
123
- | Type | `string \| Array<string> \| null` |
124
- | Required | `false` |
125
- | Default | `null` |
126
-
127
- #### infinite.previousParam
128
-
129
- Path to the previous-page cursor on the response, in the same dot or array form.
130
-
131
- | | |
132
- | -------- | --------------------------------- |
133
- | Type | `string \| Array<string> \| null` |
134
- | Required | `false` |
135
- | Default | `null` |
136
-
137
- #### infinite.cursorParam
138
-
139
- Deprecated name of a top-level response field that holds the cursor, read for both `getNextPageParam` and `getPreviousPageParam` when set. Dot paths are not supported. Use `nextParam` and `previousParam` instead.
140
-
141
- | | |
142
- | -------- | ---------------- |
143
- | Type | `string \| null` |
144
- | Required | `false` |
145
- | Default | `null` |
146
-
147
- #### infinite.getNextPageParam
148
-
149
- Source code for TanStack Query's `getNextPageParam`, copied into the generated code as written. Pass a string or a function. Use it when the next page must be computed instead of read from a field. Takes precedence over `nextParam` and `cursorParam`.
150
-
151
- | | |
152
- | -------- | ------------------------------- |
153
- | Type | `string \| PageParamFn \| null` |
154
- | Required | `false` |
155
- | Default | `null` |
156
-
157
- ```typescript
158
- infinite: {
159
- queryParam: 'page',
160
- getNextPageParam: '(lastPage) => (lastPage.hasNext ? lastPage.number + 1 : undefined)',
161
- getPreviousPageParam: '(firstPage) => (firstPage.number > 0 ? firstPage.number - 1 : undefined)',
162
- }
163
- ```
164
-
165
- `definePageParam` types the page and the page parameter, and returns the function's source. A plain function works too. Only the source is copied, so the function cannot reference variables from the config file.
166
-
167
- ```typescript [kubb.config.ts]
168
- import { definePageParam, pluginReactQuery } from '@kubb/plugin-react-query'
169
-
170
- type PetsPage = { hasNext: boolean; number: number }
171
-
172
- pluginReactQuery({
173
- hooks: true,
174
- infinite: {
175
- queryParam: 'page',
176
- getNextPageParam: definePageParam<PetsPage, number>((lastPage) => (lastPage.hasNext ? lastPage.number + 1 : undefined)),
177
- },
178
- })
179
- ```
180
-
181
- Without `getNextPageParam`, `nextParam` or `cursorParam`, the hook counts pages up from `initialPageParam` until a page is an empty array. Counting works only for array responses, so generation warns for any other response.
182
-
183
- #### infinite.getPreviousPageParam
184
-
185
- Source code for TanStack Query's `getPreviousPageParam`, copied into the generated code as written. Takes a string or a function, like `getNextPageParam`. Takes precedence over `previousParam` and `cursorParam`.
186
-
187
- | | |
188
- | -------- | ------------------------------- |
189
- | Type | `string \| PageParamFn \| null` |
190
- | Required | `false` |
191
- | Default | `null` |
192
-
193
- ### suspense
194
-
195
- Adds `useSuspenseQuery` hooks next to the regular query hooks. Pass `{}` to turn it on. Needs TanStack Query v5 or higher and [`hooks: true`](#hooks).
196
-
197
- | | |
198
- | -------- | -------------------------- |
199
- | Type | `Partial<object> \| false` |
200
- | Required | `false` |
201
- | Default | `false` |
105
+ | Default | `false` |
202
106
 
203
107
  ### query
204
108
 
205
- Which operations become queries, emitting a `queryOptions` factory by default. Pass `false` to skip, or set [`hooks`](#hooks) to also emit `useQuery`.
109
+ Which operations become queries and where TanStack Query is imported from. `false` skips query output.
206
110
 
207
111
  | | |
208
112
  | -------- | -------------------------------------------------------------------- |
@@ -212,7 +116,7 @@ Which operations become queries, emitting a `queryOptions` factory by default. P
212
116
 
213
117
  #### query.methods
214
118
 
215
- HTTP methods treated as queries.
119
+ HTTP methods treated as queries. A method in neither list gets no helper, so keep `'QUERY'` when you replace it.
216
120
 
217
121
  | | |
218
122
  | -------- | ------------------ |
@@ -220,11 +124,9 @@ HTTP methods treated as queries.
220
124
  | Required | `false` |
221
125
  | Default | `['GET', 'QUERY']` |
222
126
 
223
- Operations whose method is in neither `query.methods` nor `mutation.methods` get no hook. Keep `'QUERY'` here when overriding the list, or QUERY operations are skipped.
224
-
225
127
  #### query.importPath
226
128
 
227
- Module for the `queryOptions` and `useQuery` imports.
129
+ Module for `queryOptions`, `useQuery`, and their variants, such as a wrapper that sets a default `QueryClient`.
228
130
 
229
131
  | | |
230
132
  | -------- | ------------------------- |
@@ -234,31 +136,28 @@ Module for the `queryOptions` and `useQuery` imports.
234
136
 
235
137
  ### queryKey
236
138
 
237
- Builds the `queryKey` for each hook from the operation `node`, `casing` and `variant`. Strings are copied into the generated code as written, so wrap a string literal in `JSON.stringify(...)`.
139
+ Builds each query's `queryKey` from the operation `node` and the `variant`: `'query'`, `'suspenseQuery'`, `'infiniteQuery'`, or `'suspenseInfiniteQuery'`. The default key is `[{ url, params }, query, body]`.
238
140
 
239
141
  | | |
240
142
  | -------- | ------------------------------------------------------ |
241
143
  | Type | `(props: { node, casing, variant }) => Array<unknown>` |
242
144
  | Required | `false` |
243
- | Default | Exported `queryKeyTransformer` |
244
-
245
- `variant` is `'query'`, `'suspenseQuery'`, `'infiniteQuery'` or `'suspenseInfiniteQuery'`. The default key adds `infinite: true` to infinite variants so they never share a cache entry with the plain query.
145
+ | Default | `queryKeyTransformer` |
246
146
 
247
- ```typescript
248
- queryKey: ({ node, variant }) => [JSON.stringify({ variant, operationId: node.operationId })]
249
- ```
147
+ > [!WARNING]
148
+ > Returned strings are copied into the generated code as written, so wrap a string literal in `JSON.stringify(...)`.
250
149
 
251
- To extend the default key instead of replacing it, import `queryKeyTransformer` and spread its result. The default segments can change between releases, so spread them instead of reading single entries.
150
+ To extend the default, spread it. See [TanStack Query keys](https://kubb.dev/extensions/kubb-labs/plugin-react-query/guides/query-keys).
252
151
 
253
152
  ```typescript
254
153
  import { queryKeyTransformer } from '@kubb/plugin-react-query'
255
154
 
256
- queryKey: (props) => [JSON.stringify(props.node.operationId), ...queryKeyTransformer(props)]
155
+ queryKey: (props) => [JSON.stringify('v2'), ...queryKeyTransformer(props)]
257
156
  ```
258
157
 
259
158
  ### mutation
260
159
 
261
- Which operations become mutations, emitting a `mutationOptions` factory by default. Set `false` to skip, or set [`hooks`](#hooks) to also emit `useMutation`.
160
+ Which operations become mutations and where TanStack Query is imported from. `false` skips mutation output.
262
161
 
263
162
  | | |
264
163
  | -------- | -------------------------------------------------------------------------------------- |
@@ -268,7 +167,7 @@ Which operations become mutations, emitting a `mutationOptions` factory by defau
268
167
 
269
168
  #### mutation.methods
270
169
 
271
- HTTP methods treated as mutations.
170
+ HTTP methods treated as mutations. A method also in `query.methods` stays a query.
272
171
 
273
172
  | | |
274
173
  | -------- | ------------------------------------ |
@@ -278,7 +177,7 @@ HTTP methods treated as mutations.
278
177
 
279
178
  #### mutation.importPath
280
179
 
281
- Module for the `mutationOptions` and `useMutation` imports.
180
+ Module for `mutationOptions` and `useMutation`.
282
181
 
283
182
  | | |
284
183
  | -------- | ------------------------- |
@@ -288,34 +187,155 @@ Module for the `mutationOptions` and `useMutation` imports.
288
187
 
289
188
  ### mutationKey
290
189
 
291
- Builds the `mutationKey` for each mutation hook, for batched invalidations or `useMutationState`. Same props and string-inlining caveat as `queryKey`, with `variant` set to `'mutation'`.
190
+ Builds each mutation's `mutationKey`, for batched invalidations or `useMutationState`. It works like [`queryKey`](#querykey) with `variant: 'mutation'`. The default key is `[{ url }]`.
292
191
 
293
192
  | | |
294
193
  | -------- | ------------------------------------------------------ |
295
194
  | Type | `(props: { node, casing, variant }) => Array<unknown>` |
296
195
  | Required | `false` |
297
- | Default | Exported `mutationKeyTransformer` |
298
-
299
- Extend the default the same way with `mutationKeyTransformer`:
196
+ | Default | `mutationKeyTransformer` |
300
197
 
301
198
  ```typescript
302
199
  import { mutationKeyTransformer } from '@kubb/plugin-react-query'
303
200
 
304
- mutationKey: (props) => [JSON.stringify(props.node.operationId), ...mutationKeyTransformer(props)]
201
+ mutationKey: (props) => [JSON.stringify('v2'), ...mutationKeyTransformer(props)]
202
+ ```
203
+
204
+ ### infinite
205
+
206
+ Adds `infiniteQueryOptions` and `useInfiniteQuery` to each query operation that takes the [`infinite.queryParam`](#infinitequeryparam) parameter. Needs [`hooks: true`](#hooks).
207
+
208
+ | | |
209
+ | -------- | ---------------------------- |
210
+ | Type | `Partial<Infinite> \| false` |
211
+ | Required | `false` |
212
+ | Default | `false` |
213
+
214
+ > [!WARNING]
215
+ > Set `nextParam` or `getNextPageParam`. Otherwise `hasNextPage` never turns false for a non-array response. See [Infinite queries](https://kubb.dev/extensions/kubb-labs/plugin-react-query/guides/infinite-queries#hasnextpage-never-turns-false).
216
+
217
+ #### infinite.queryParam
218
+
219
+ Query parameter that carries the page cursor.
220
+
221
+ | | |
222
+ | -------- | -------- |
223
+ | Type | `string` |
224
+ | Required | `false` |
225
+ | Default | `'id'` |
226
+
227
+ #### infinite.initialPageParam
228
+
229
+ `pageParam` of the first fetch. Give it the type of the query parameter.
230
+
231
+ | | |
232
+ | -------- | --------- |
233
+ | Type | `unknown` |
234
+ | Required | `false` |
235
+ | Default | `0` |
236
+
237
+ #### infinite.nextParam
238
+
239
+ Path to the next cursor on the response, in dot notation (`'pagination.next.id'`) or array form (`['pagination', 'next', 'id']`).
240
+
241
+ | | |
242
+ | -------- | --------------------------------- |
243
+ | Type | `string \| Array<string> \| null` |
244
+ | Required | `false` |
245
+ | Default | `null` |
246
+
247
+ #### infinite.previousParam
248
+
249
+ Path to the previous cursor, in the same forms as `nextParam`.
250
+
251
+ | | |
252
+ | -------- | --------------------------------- |
253
+ | Type | `string \| Array<string> \| null` |
254
+ | Required | `false` |
255
+ | Default | `null` |
256
+
257
+ #### infinite.getNextPageParam
258
+
259
+ Source of TanStack Query's `getNextPageParam`, for a next page you must compute. Pass a string, a function, or `definePageParam`, which types the page and the page parameter.
260
+
261
+ | | |
262
+ | -------- | ------------------------------- |
263
+ | Type | `string \| PageParamFn \| null` |
264
+ | Required | `false` |
265
+ | Default | `null` |
266
+
267
+ > [!NOTE]
268
+ > Only the function's source is copied, so it cannot use variables from the config file.
269
+
270
+ ```typescript [kubb.config.ts]
271
+ import { definePageParam, pluginReactQuery } from '@kubb/plugin-react-query'
272
+
273
+ type PetsPage = { hasNext: boolean; number: number }
274
+
275
+ pluginReactQuery({
276
+ hooks: true,
277
+ infinite: {
278
+ queryParam: 'page',
279
+ getNextPageParam: definePageParam<PetsPage, number>((lastPage) => (lastPage.hasNext ? lastPage.number + 1 : undefined)),
280
+ },
281
+ })
305
282
  ```
306
283
 
284
+ #### infinite.getPreviousPageParam
285
+
286
+ Source of TanStack Query's `getPreviousPageParam`, in the same forms as `getNextPageParam`.
287
+
288
+ | | |
289
+ | -------- | ------------------------------- |
290
+ | Type | `string \| PageParamFn \| null` |
291
+ | Required | `false` |
292
+ | Default | `null` |
293
+
294
+ #### infinite.cursorParam
295
+
296
+ Deprecated. Use `nextParam` and `previousParam`. A top-level response field, not a dot path, that holds the cursor for both directions.
297
+
298
+ | | |
299
+ | -------- | ---------------- |
300
+ | Type | `string \| null` |
301
+ | Required | `false` |
302
+ | Default | `null` |
303
+
304
+ ### suspense
305
+
306
+ Pass `{}` to add `useSuspenseQuery` hooks, plus `useSuspenseInfiniteQuery` when [`infinite`](#infinite) is set. Needs [`hooks: true`](#hooks).
307
+
308
+ | | |
309
+ | -------- | ----------------- |
310
+ | Type | `object \| false` |
311
+ | Required | `false` |
312
+ | Default | `false` |
313
+
307
314
  ### customOptions
308
315
 
309
- Routes every hook through a custom function that returns extra options such as `onSuccess` or `select`. Also emits a `HookOptions` type that maps each hook name to its options type. Needs [`hooks: true`](#hooks).
316
+ Calls one function of yours from every hook to share options such as `staleTime`. Kubb also writes a `HookOptions` type, keyed by hook name. Needs [`hooks: true`](#hooks).
310
317
 
311
318
  | | |
312
319
  | -------- | --------------------------------------- |
313
- | Type | `{ importPath: string, name?: string }` |
320
+ | Type | `{ importPath: string; name?: string }` |
314
321
  | Required | `false` |
315
322
 
323
+ Each hook calls it with `{ hookName, operationId }`, and per-call options win over its result:
324
+
325
+ ```typescript [src/gen/hooks/useCustomHookOptions.ts]
326
+ import type { HookOptions } from './HookOptions'
327
+
328
+ export function useCustomHookOptions({ hookName }: { hookName: keyof HookOptions; operationId: string }) {
329
+ if (hookName === 'useGetPetById') {
330
+ return { staleTime: 60_000 } satisfies HookOptions['useGetPetById']
331
+ }
332
+ return {}
333
+ }
334
+ ```
335
+
316
336
  #### customOptions.importPath
317
337
 
318
- Module of the custom-options hook, imported as a named import. The path resolves from the generated hooks folder. When the module does not exist yet, Kubb writes a starter file there, but only when `importPath` stays inside the plugin's output folder. The starter file imports `HookOptions` from the generated `HookOptions.ts` file. A module outside the output folder is never created.
338
+ Module of the function, resolved from the generated hooks folder. If the path is inside the plugin's output folder and the file is missing, Kubb writes a starter file.
319
339
 
320
340
  | | |
321
341
  | -------- | ---------------------------------- |
@@ -324,7 +344,7 @@ Module of the custom-options hook, imported as a named import. The path resolves
324
344
 
325
345
  #### customOptions.name
326
346
 
327
- Exported name of the custom-options hook.
347
+ Exported name of the function.
328
348
 
329
349
  | | |
330
350
  | -------- | ------------------------ |
@@ -332,19 +352,9 @@ Exported name of the custom-options hook.
332
352
  | Required | `false` |
333
353
  | Default | `'useCustomHookOptions'` |
334
354
 
335
- ### hooks
336
-
337
- When `false` (the default), only the `queryOptions`, `mutationOptions`, `queryKey` and `mutationKey` helpers are written, using factory imports that work across TanStack Query adapters. Set `true` to also generate `useQuery`, `useSuspenseQuery`, `useInfiniteQuery`, `useSuspenseInfiniteQuery`, and `useMutation`.
338
-
339
- | | |
340
- | -------- | --------- |
341
- | Type | `boolean` |
342
- | Required | `false` |
343
- | Default | `false` |
344
-
345
355
  ### resolver
346
356
 
347
- Overrides generated file and symbol names. Omitted members keep `resolverReactQuery`. The shared members (`name`, `file`, `imports`) and the `this` context are described under [`resolver`](https://kubb.dev/docs/5.x/reference/plugin-options#resolver).
357
+ Overrides generated names. Members you leave out keep `resolverReactQuery`. See [`resolver`](https://kubb.dev/docs/5.x/reference/plugin-options#resolver) for the shared members.
348
358
 
349
359
  | | |
350
360
  | -------- | ----------------------------------- |
@@ -357,20 +367,23 @@ Overrides generated file and symbol names. Omitted members keep `resolverReactQu
357
367
  type ResolverReactQueryPatch = {
358
368
  // plus the shared name, file, and imports members
359
369
  query?: {
360
- name?(node: OperationNode): string // → 'useGetPetById'
361
- optionsName?(node: OperationNode): string // → 'getPetByIdQueryOptions'
362
- keyName?(node: OperationNode): string // → 'getPetByIdQueryKey'
363
- keyTypeName?(node: OperationNode): string // → 'GetPetByIdQueryKey'
370
+ name?(node: OperationNode): string // 'useGetPetById'
371
+ optionsName?(node: OperationNode): string // 'getPetByIdQueryOptions'
372
+ keyName?(node: OperationNode): string // 'getPetByIdQueryKey'
373
+ keyTypeName?(node: OperationNode): string // 'GetPetByIdQueryKey'
364
374
  }
365
- // suspenseQuery, infiniteQuery, suspenseInfiniteQuery: same members as query
375
+ // same members, with a Suspense, Infinite, or SuspenseInfinite suffix
376
+ suspenseQuery?: ResolverReactQueryPatch['query']
377
+ infiniteQuery?: ResolverReactQueryPatch['query']
378
+ suspenseInfiniteQuery?: ResolverReactQueryPatch['query']
366
379
  mutation?: {
367
- name?(node: OperationNode): string // → 'useUpdatePet'
368
- optionsName?(node: OperationNode): string
369
- keyName?(node: OperationNode): string
380
+ name?(node: OperationNode): string // 'useUpdatePet'
381
+ optionsName?(node: OperationNode): string // 'updatePetMutationOptions'
382
+ keyName?(node: OperationNode): string // 'updatePetMutationKey'
370
383
  }
371
384
  hook?: {
372
- optionsName?(): string
373
- customOptionsName?(): string
385
+ optionsName?(): string // 'HookOptions'
386
+ customOptionsName?(): string // 'getCustomHookOptions'
374
387
  }
375
388
  }
376
389
  ```
package/docs/overview.md CHANGED
@@ -1,29 +1,31 @@
1
1
  # React Query
2
2
 
3
- The React Query plugin generates TanStack Query helpers for React from your OpenAPI spec. By default you get `queryOptions` and `mutationOptions` factories with their query and mutation keys. Set `hooks: true` to also get `useQuery` and `useMutation` hooks.
3
+ The React Query plugin generates TanStack Query `queryOptions` and `mutationOptions` factories with cache keys from your OpenAPI spec, plus optional hooks.
4
4
 
5
- The plugin ships no HTTP client. Each helper calls a function generated by a client plugin.
5
+ - `hooks: true` adds `useQuery` and `useMutation` hooks, with suspense and infinite variants on request.
6
+ - The plugin ships no HTTP client. Each helper calls a function from your client plugin.
6
7
 
7
8
  ## Setup
8
9
 
9
- - Add `@kubb/plugin-ts`, or `pluginZod({ inferred: 'codec' })` for codec types, and a client plugin: `@kubb/plugin-fetch`, `@kubb/plugin-axios`, or `@kubb/plugin-client`. Leave `sdk` unset on the client plugin.
10
- - Install `@tanstack/react-query` 5.82 or a later v5 release in the app that uses the generated code.
11
- - Configure validation on the client plugin, not here.
10
+ - Add `@kubb/plugin-ts`, or `pluginZod({ inferred: 'codec' })` with `zod` v4 in the app.
11
+ - Add one client plugin: `@kubb/plugin-fetch`, `@kubb/plugin-axios`, or `@kubb/plugin-client`. Leave `sdk` unset on it.
12
+ - Install `@tanstack/react-query` 5.82 or a later v5 release in the app.
13
+ - Set the base URL, auth, and validation on the client plugin. With `@kubb/plugin-client`, your client module owns them.
12
14
 
13
15
  ## Options worth knowing
14
16
 
15
- - `client` picks `fetch`, `axios`, or `client` when more than one client plugin is registered. A single client plugin is detected automatically.
16
- - `hooks` adds the `use*` hooks on top of the option factories.
17
- - `suspense: {}` adds `useSuspenseQuery` hooks. It needs `hooks: true`.
18
- - `infinite` adds `useInfiniteQuery` hooks for pagination. It needs `hooks: true` and covers the operations that take the query parameter named in `infinite.queryParam`, which defaults to `'id'`.
19
- - `query` and `mutation` set which HTTP methods become queries (`GET`, `QUERY`) and mutations (`POST`, `PUT`, `PATCH`, `DELETE`). They also set where the TanStack Query runtime is imported from. Set either to `false` to skip those helpers.
20
- - `customOptions: { importPath }` routes every hook through your own function that returns extra options. It needs `hooks: true`. `importPath` resolves from the generated hooks folder. Kubb creates a starter file there when none exists and the path stays inside the plugin's output folder.
21
-
22
- See [Options](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options) for every option and its default.
17
+ | Option | What it does |
18
+ | ------------------- | ------------------------------------------------------------------------------------------------ |
19
+ | `client` | Picks `'fetch'`, `'axios'`, or `'client'` when several client plugins are registered. |
20
+ | `hooks` | Adds the `use*` hooks. `infinite`, `suspense`, and `customOptions` need it. |
21
+ | `query`, `mutation` | Which HTTP methods become queries and mutations. `false` skips them. |
22
+ | `infinite` | Adds infinite queries to operations with the `infinite.queryParam` parameter, `'id'` by default. |
23
+ | `suspense` | `{}` adds `useSuspenseQuery` hooks. |
24
+ | `customOptions` | Routes every hook through your function for shared options. |
23
25
 
24
26
  ## Example
25
27
 
26
- The config below generates `useQuery` and `useMutation` hooks that call the Fetch client.
28
+ This config generates hooks that call the Fetch client.
27
29
 
28
30
  ```typescript [kubb.config.ts]
29
31
  import { defineConfig } from 'kubb/config'
@@ -43,3 +45,5 @@ export default defineConfig({
43
45
  ],
44
46
  })
45
47
  ```
48
+
49
+ See [Options](https://kubb.dev/extensions/kubb-labs/plugin-react-query/options) for every option. Next, [call operations](https://kubb.dev/extensions/kubb-labs/plugin-react-query/guides/calling-operations).
@@ -4,7 +4,7 @@
4
4
  "name": "plugin-react-query",
5
5
  "export": "pluginReactQuery",
6
6
  "displayName": "React Query",
7
- "description": "Generates typed TanStack Query helpers for React from your OpenAPI spec: queryOptions and mutationOptions factories with their cache keys. Set hooks to also get useQuery and useMutation hooks, plus useInfiniteQuery when infinite is set.",
7
+ "description": "Generates typed TanStack Query helpers for React from your OpenAPI spec: queryOptions and mutationOptions factories with cache keys, plus optional hooks.",
8
8
  "category": "framework",
9
9
  "icon": {
10
10
  "light": "https://kubb.dev/feature/tanstack.svg"
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "optionalDependencies": ["plugin-ts", "plugin-zod"],
21
21
  "compatibility": {
22
- "kubb": ">=5.18.4",
22
+ "kubb": ">=5.19.0",
23
23
  "node": ">=22.22"
24
24
  },
25
25
  "role": "consumer",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubb/plugin-react-query",
3
- "version": "5.10.3",
3
+ "version": "5.10.4",
4
4
  "description": "Generate type-safe TanStack Query (React Query) hooks with Kubb. Covers useQuery, useMutation, useInfiniteQuery, and queryOptions with full TypeScript support.",
5
5
  "keywords": [
6
6
  "code-generation",
@@ -82,7 +82,7 @@
82
82
  },
83
83
  "dependencies": {
84
84
  "@kubb/kit": "^5.20.1",
85
- "@kubb/plugin-ts": "^5.5.3",
85
+ "@kubb/plugin-ts": "^5.5.4",
86
86
  "@kubb/renderer-jsx": "^5.20.1"
87
87
  },
88
88
  "devDependencies": {
@@ -90,7 +90,7 @@
90
90
  "@internals/shared": "0.0.0",
91
91
  "@internals/tanstack-query": "0.0.0",
92
92
  "@internals/utils": "0.0.0",
93
- "@kubb/plugin-client": "6.0.3",
93
+ "@kubb/plugin-client": "6.0.4",
94
94
  "@tanstack/react-query": "^5.107.1"
95
95
  },
96
96
  "peerDependencies": {