---
layout: doc
title: Kubb clients 6.0
description: "@kubb/plugin-fetch, @kubb/plugin-axios, and @kubb/plugin-client 6.0 run on one shared core: the same interceptors and types for fetch and axios, any library that returns a Response as a transport, and cookie parameters."
outline: deep
image: /blog/clients-v6/cover.svg
date: 2026-11-08
---

> Index of every page on this site: [llms.txt](https://kubb.dev/llms.txt)

![Kubb clients 6.0](/blog/clients-v6/cover.svg)

# Kubb clients 6.0

`@kubb/plugin-fetch`, `@kubb/plugin-axios`, and `@kubb/plugin-client` 6.0 run the fetch and axios clients on one shared core. Both clients now have the same interceptors, client factory, error class, and types. A transport only sends the request, so another HTTP library such as [ky](https://github.com/sindresorhus/ky) takes one line:

```ts [src/client.ts]
import ky from 'ky'
import { client, toRequestInit } from './gen/.kubb/client'

client.setConfig({
  transport: (request) => ky(request.url, { ...toRequestInit(request), throwHttpErrors: false, retry: 0 }),
})
```

Code that calls the generated functions or hooks keeps working. Most projects only regenerate.

## One core for fetch and axios

In v5 the two clients only looked alike. The core was already shared, but axios wrapped it in its own client, interceptors, and transport types, so an interceptor written for fetch did not run on axios. v6 drops that layer. The core builds the URL, serializes parameters, resolves auth, runs validation and interceptors, and splits success from error. Each package adds only a transport, the function that sends the resolved request.

Now one interceptor runs on both clients, because both hand it the core's `ResolvedRequest`, `TransportResult`, and `ResponseError`:

```ts [src/client.ts]
client.interceptors.request.use((request) => {
  request.headers['X-Request-ID'] = crypto.randomUUID()
  return request
})
```

On axios, native handlers that need `InternalAxiosRequestConfig` or `AxiosResponse` go on the new `axiosInstance` export, the instance the default transport sends through.

## Bring your own HTTP library

A transport can now return a plain web `Response`. The core parses it the same way the fetch client does, by `responseType` and `Content-Type`, and response interceptors still see the parsed result. `toRequestInit(request)` builds the `RequestInit` that fetch and ky take.

This works for any library that hands back an unread `Response`, including a polyfill such as node-fetch. Keep the library from throwing on an error status, as `throwHttpErrors: false` does for ky, so the client can return or throw its typed `ResponseError`.

Axios is the exception, since it never returns a web `Response`. Its transport still decodes `responseType` maps itself, so native axios interceptors see parsed data, as they did in v5. That is one more code path to keep in step, but it means handlers you move from v5 keep working.

Set a transport once with `client.setConfig({ transport })`, per client with `createClient({ transport })`, or per call with `transport`. See [Use a custom transport](/extensions/kubb-labs/plugin-fetch/guides/transport).

## Cookie parameters

An operation with an `in: cookie` parameter gets a `<Name>Cookies` type and a `cookies` option. The call, SDK method, query hooks, MCP tool, and Cypress request all take it, and Zod and faker get the same group:

```ts [src/session.ts]
await getSession({ cookies: { session_id: 'abc' } })
```

`cookies` is always optional, because a browser sends its cookies itself. Query keys leave it out, as they do headers. A query key is written into SSR payloads, persisted caches, and devtools, and a session cookie has no business in any of them.

## Fixes

- Stream calls send `Accept: text/event-stream`. Before, axios asked for JSON, so a server that picks its format from `Accept`, such as Wikimedia EventStreams, answered with JSON and the stream never ended.
- Axios honors `credentials`: `include`, `same-origin`, and `omit`.
- On the Node http adapter, a `responseType: 'blob'` call returns a `Blob` instead of a corrupted string.
- Both clients read bodies the same way. A body labeled JSON that is not JSON comes back as text, and a 204, 205, or 304 is `undefined` on both.
- A failed blob or arraybuffer call parses a JSON or text error body, so `error` is the error object.
- A failed call with an empty body reports `{ status, statusText }` as its `error`, so `if (error)` and `.unwrap()` see the failure.
- An aborted stream stops before the next event and rejects with the signal's reason, so `lastEventId` stays at the last event you read.
- On fetch, the Next.js `next` option reaches `fetch`, so caching options take effect.

## Breaking changes

Regenerate, run `tsc --noEmit`, and match each error to a row:

| Your code | Client | What to do |
| --- | --- | --- |
| Calls generated functions, SDK methods, or hooks | all | Regenerate. Nothing else. |
| Imports from `templates/fetch.ts`, `templates/axios.ts`, or `templates/core.ts` | fetch, axios | Import types from `templates/client.ts`, and the shared runtime from `@kubb/plugin-client/templates/*`. |
| Uses `FetchOptions` or `AxiosOptions` | fetch, axios | Rename it to `NativeOptions`. |
| Names `EventStreamResult<Pet>`, `StreamResult`, or `UnwrappedStreamResult` | fetch, axios | Wrap the payload: `EventStreamResult<ServerSentEvent<Pet>>`. |
| Registers handlers on `client.interceptors` | axios | Move them to `axiosInstance.interceptors`, or rewrite them for the core objects. |
| Passes an axios instance as `transport`, or sets `defaultTransport.defaults` | axios | Wrap it with `createAxiosTransport(instance)`, and set defaults on `axiosInstance`. |
| Wrote your own client with stream helpers | plugin-client | Take the full event type, export `ServerSentEvent`, and add `contentType` to `RequestConfig`. |

Three axios changes compile, so check them by hand. `client.interceptors.request` handlers run in the order you register them. `client.interceptors.error` runs only for a `ResponseError`, not for a network error. A child from `createClient()` inherits the transport of `client`.

## Upgrade

v6 needs Kubb 5.16.0 or later. Upgrade Kubb and your client plugin together, then regenerate:

```shell [Terminal]
npm install --save-dev kubb@latest @kubb/plugin-fetch@6
npx kubb generate
npx tsc --noEmit
```

Use `@kubb/plugin-axios@6` for the axios client. The query plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, and `@kubb/plugin-mcp`) work with both v5 and v6, so you can upgrade them separately.

Each client has a migration guide with a before and after for every change:

- [Migrate @kubb/plugin-fetch to v6](/extensions/kubb-labs/plugin-fetch/guides/migrate-to-v6)
- [Migrate @kubb/plugin-axios to v6](/extensions/kubb-labs/plugin-axios/guides/migrate-to-v6)
- [Migrate @kubb/plugin-client to v6](/extensions/kubb-labs/plugin-client/guides/migrate-to-v6)

Feedback goes to [GitHub](https://github.com/kubb-labs/plugins/issues) or [Discord](https://discord.gg/shfBFeczrm).

## Sitemap

See the full [sitemap](https://kubb.dev/sitemap.md) for all pages.
