Beta You're reading the docs for Kubb v5, which is currently in beta. View the stable v4 docs
Skip to content

Migration: @kubb/plugin-react-query

Part of the v4 → v5 migration guide. For the full option reference, see @kubb/plugin-react-query.

resolver.resolveName replaces transformers.name.

Removed: paramsType, pathParamsType, paramsCasing

These three options are gone, including client.paramsCasing. Each hook now takes its parameters as a single grouped options object shaped as { path, query, body, headers }, with camelCase property names. This matches the shape @kubb/plugin-fetch already used. Query params move under query, path params under path, the request body under body, and header params under headers.

Diff
diff
  pluginReactQuery({
-   paramsType: 'object',
-   pathParamsType: 'object',
-   paramsCasing: 'camelcase',
  })

Update the call sites. Query params move into query, and path params move into path. When an operation has a required parameter in a group, that group (path, query, or headers) is required too, so an incomplete call fails to compile.

typescript
useFindPets({ status: 'available' })
useGetPet(petId)
useUpdatePet().mutate({ petId, data: pet })
typescript
useFindPets({ query: { status: 'available' } })
useGetPet({ path: { petId } })
useUpdatePet().mutate({ path: { petId }, body: pet })

The first argument is typed Omit<XxxRequestConfig, 'url'>, the RequestConfig type @kubb/plugin-ts generates. The trailing config argument is unchanged.

Generated output

@kubb/plugin-vue-query shares these changes. Both plugins also pick up the renames from the client plugin (*Data, *Response, *Status<code>).

The exported *MutationKey type alias is gone. Use the runtime helper when you need the key:

Diff
diff
- export type CreateUserMutationKey = ReturnType<typeof createUserMutationKey>
- export const createUserMutationKey = () => [{ url: '/user' }] as const
+ export const createUserMutationKey = () => [{ url: '/user' }] as const

Mutation and query TData narrows to 2xx responses

The TData generic on useMutation, useQuery, useInfiniteQuery, useSuspenseQuery, and their *Options helpers now points at the union of 2xx response status types instead of the full response alias. That matches TanStack Query's contract, where TData is the resolved success value and errors flow through TError.

Diff
diff
  export function useAddPet<TContext>(
    options: {
      mutation?: MutationObserverOptions<
-       AddPetResponse,
+       AddPetStatus200,
        ResponseErrorConfig<AddPetStatus405>,
        { data: AddPetData },
        TContext
      > & { client?: QueryClient }
      client?: Partial<RequestConfig<AddPetData>> & { client?: typeof client }
    } = {},
  ) { /* ... */ }

Call sites that previously needed as casts or 'id' in res checks compile directly:

Generated output
typescript
const pet = await mutateAsync({ body: { name: 'Rex' } })
pet.id // typed as Pet.id, no narrowing required

The change covers queryFn, queryOptions, and the hook generics together. No config flag brings back the old behavior. If your client returns non-2xx bodies as resolved data instead of throwing, wrap it to throw so TanStack Query's error / onError path fires. The previous typing was silently broken at runtime.

No auto enabled guard

v4 generated an enabled guard from the required path and query parameters: enabled: !!path?.petId in React Query, enabled: () => !!toValue(path?.petId) in Vue Query. Those parameters were already required, so !!path.petId was always true. The guard disabled nothing. It read like a safety net and did no work.

v5 removes it. The path, query, and headers groups are required in the generated queryKey, queryOptions, and hook signatures whenever the operation has a required parameter in that group, and nothing emits enabled for you. The query key types only the groups it reads, so a required headers parameter never leaks onto the key.

Diff
diff
  export function getPetByIdQueryOptions({ path }: Omit<GetPetByIdRequestConfig, 'url'>, config: Partial<RequestConfig> & { client?: Client } = {}) {
    const queryKey = getPetByIdQueryKey({ path })
    return queryOptions<GetPetByIdStatus200, ResponseErrorConfig<GetPetByIdStatus400 | GetPetByIdStatus404>, GetPetByIdStatus200, typeof queryKey>({
-     enabled: !!path?.petId,
      queryKey,
      queryFn: async ({ signal }) => {
        return getPetById({ path }, { ...config, signal: config.signal ?? signal })
      },
    })
  }

To defer or disable a query, set TanStack Query's own enabled (or pass skipToken) through the hook options:

Generated output
typescript
// keep the query disabled until petId resolves
useGetPetById({ path: { petId } }, { query: { enabled: !!petId } })

NOTE

Suspense hooks always run, so they never had an enabled guard and are unchanged.