Skip to content
Axios logo

Axios

Officialv6.0.4

@kubb/plugin-axios

kubb add plugin-axios

Migrate to v6

Upgrade @kubb/plugin-axios from v5 to v6, which runs on the shared client core from @kubb/plugin-client with axios as the transport.

Code that only calls the generated functions, SDK classes, or hooks needs a regenerate. Most other edits land in the file that configures client.

Upgrade steps

Upgrade the packages

Upgrade kubb and @kubb/plugin-axios together. v6 needs Kubb 5.19.0 or later:

npm install --save-dev kubb@latest @kubb/plugin-axios@6

Regenerate

This rewrites .kubb/ and the operation files:

npx kubb generate

Run the type checker

Most breaking changes show up as compile errors:

npx tsc --noEmit

Fix each error

Find its row in What to change. Then check the behavior changes, which compile without errors.

What to change

You usedNow
@kubb/plugin-axios/templates/axios.ts@kubb/plugin-axios/templates/client.ts. Types only.
@kubb/plugin-axios/templates/core.ts, serializers.ts, or standardSchema.ts@kubb/plugin-client/templates/*. Types only.
client.interceptors handlers that read axios objectsMove them to getAxiosInstance(), or rewrite them. See Interceptors.
An axios instance as transportcreateAxiosTransport(instance)
defaultTransport.defaultsgetAxiosInstance().defaults. defaultTransport is now a function.
A transport that returns { data, status, ... }Return a web Response, or { webResponse, request, response }. Response interceptors get a DecodedResponse, the shape TransportResult had.
client.interceptors.error handlers that read statusNarrow with ResponseError.is first. They also get network errors (AxiosError), aborts, and validation errors.
AxiosOptionsNativeOptions, the same type: AxiosRequestConfig.
validateStatusRemoved. The client splits success from error by status.
AxiosTransport, InterceptorChannelRemoved. createClientCore, ClientInstance, and Interceptors come from the core.
InterceptorFnThe core's InterceptorFn<T>, which gets the core objects.
defaultQuerySerializerserializeQuery, or serializeStyledQuery for a styled query parameter.
defaultPathSerializerserializePath, plus serializeStyledPath for a label or matrix parameter.
applyHeaderStylesserializeHeaders
codecsmediaTypes. See Media types.
Codec, Deserializer, ContentBodySerializer, BodySerializerMediaType
serializer.body, defaultBodySerializer, isDefaultJsonBodyA mediaTypes entry, or defaultMediaTypes.
responseType, ResponseType, ResponseTypeMap, resolveResponseTypeparseAs and ParseAs, such as parseAs: 'blob', or a media type.
formMediaTypesmultipartFormData and urlencoded, written to .kubb/mediaTypes/ when the spec has a form body.
CallResult, isStreamed, getResponseContentTypeRemoved. Read result.contentType.
toEventStream, EventStreamCall, EventStreamResultclient.stream and StreamResult. See Streams.
ServerSentEvent, TypedEvent, or StreamResult from .kubb/sse.tsImport them from .kubb/client.ts.
A …Status2XX type, or a '2XX' key in a responses record or validator.errorLower case: …Status2xx and '2xx'. Upper-case keys are ignored.
clients/payment/ for tag payment with parent: storeclients/store/payment/. To keep one folder per tag, pass group: { type: 'tag', name: ({ group }) => group }.
types/Point.ts for operation point next to schema Pointtypes/PointOperation.ts, whenever a schema file has the same name, in any case.
new pet(config) with sdk and group: { type: 'tag' }new PetClient(config). Files and root class fields, such as pet, stay the same.
clientGenerator from @kubb/plugin-axiosRemoved. Build on setupClient and clientDependencies from @kubb/plugin-client.

Runtime values such as client still come from the generated .kubb/client.ts. The common rewrites:

 import type { RequestConfig } from '@kubb/plugin-axios/templates/axios.ts'
 import type { ResolvedRequest } from '@kubb/plugin-axios/templates/core.ts'
 import type { RequestConfig } from '@kubb/plugin-axios/templates/client.ts'
 import type { ResolvedRequest } from '@kubb/plugin-client/templates/core.ts'

Interceptors

client.interceptors handlers now get the core objects: ResolvedRequest, DecodedResponse, and the error. The quickest fix moves each handler, unchanged, to getAxiosInstance(), the instance v5 called defaultTransport:

client.interceptors.request.use((config) => {
  config.headers.set('X-Request-ID', crypto.randomUUID())
  return config
})

Native response interceptors see the raw bytes, not the decoded body. The core form also works with @kubb/plugin-fetch.

Media types

serialize(body, contentType) becomes encode(body, context), with the type in context.contentType. decode(response, context) gets the Response, not a parsed body, so read the body yourself:

 client.setConfig({ codecs: { 'application/xml': { deserialize: (raw) => parseXml(raw as string) } } })
 client.setConfig({ mediaTypes: { 'application/xml': { decode: async (response) => parseXml(await response.text()) } } })

A key can be a pattern, such as text/*. See Built-in media types.

Streams

client.stream returns the stream directly:

 const { stream } = await toEventStream(client({ method: 'GET', url: '/events', responseType: 'stream' }), { shape: 'data' })
 const { stream } = await client.stream({ method: 'GET', url: '/events', eventShape: 'data' })

StreamResult and UnwrappedStreamResult take the responses record and the full event type, so wrap a payload in ServerSentEvent. A TypedEvent<…> stays as is:

 let result: EventStreamResult<PetEvent>
 let result: StreamResult<StreamPetsResponses, true, ServerSentEvent<PetEvent>>

An operation whose only success type is JSON Lines returns a StreamResult with one parsed item per line, not a string. For stream responses with headers, see Stream result types.

Behavior changes

These compile without errors, so check them by hand:

Areav5v6
Clientclient.interceptors.request handlers ran in reverse, as in axios.They run in registration order.
ClientA child from createClient() got its own axios.create().It inherits the transport of client.
RequestsA path or operation with its own servers used the client's baseURL.It calls its own server, unless the call sets baseURL. See Set the base URL.
RequestsAn object query parameter without style: filter[name]=odie.name=odie&age=3. Set style: deepObject in the spec to keep the brackets.
RequestsAn array or object header without style went out as JSON.X-Ids: 3,4
RequestsA space in a query value: +.%20
RequestsspaceDelimited and pipeDelimited repeated the key.explode defaults to false, so an array goes out as ids=1%7C2.
RequestsA style: cookie parameter was percent-encoded.Sent as is. A value it cannot send, such as one with ;, throws a TypeError.
ResponsesInvalid JSON under a JSON media type became a string.Throws a ParseError. Catch it, or register a lenient application/json entry.
ResponsesAn aborted call or stream rejected with axios's CanceledError.It rejects with the signal's reason.

Also new in v6:

  • Requests: body: null is sent as JSON null. A stream call sends Accept: text/event-stream, or its JSON Lines media type. An auth requirement with several schemes sends all of them.
  • Responses: an empty body is undefined for every media type, text included. A failed call without a body has { status, statusText } as error. Every result and thrown ResponseError has the response Headers as headers.
  • Validation: ResponseError.data holds the validated body.
  • Types: a 2xx key is a success, a range key such as 4xx types status as the codes it covers, and an exact code such as 200 wins over a range. XML, YAML, and CSV responses are string, so JSON and XML together give data: Pet | string. Image, audio, video, PDF, and application/octet-stream responses are Blob, even with type: string. result.contentType is the bare media type, such as 'text/html'.
  • Serializers: each has its own file in .kubb/serializers/, and setConfig({ serializer }) merges per key. A createClientCore client gets no serializers, so pass them in its serializer config.
  • Servers: server.index no longer counts a server without a url.

If you upgrade from 5.12 or earlier, also check these changes.

Query plugins

@kubb/plugin-react-query, @kubb/plugin-vue-query, @kubb/plugin-swr, and @kubb/plugin-mcp work with v5 and v6, so you can upgrade them separately. They skip hooks for JSON Lines and event stream operations.

Less common cases

New in v6

These v6 features need no code change:

  • Validation: validator.error takes a map by status, such as { 404: getPetStatus404Schema }, to check an error body against the schema of its status.
  • Serializers: serializer.header and serializer.cookie are new. See Override parameter serialization.
  • Servers: named servers are exported as servers from .kubb/client.ts. See Named servers.

From 5.12 or earlier

These changed in 5.13 and compile without errors:

  • A stream call keeps the adapter set on the call or instance, such as a test mock. Only a browser call with the default adapters switches to the fetch adapter.
  • A successful stream call with a non-stream body, such as JSON, throws a TypeError with the body as its cause. A successful answer with no body still ends as an empty stream.
  • An event that does not end with a blank line is dropped, as in browser EventSource, so end every event with one.

SDK root class

With an SDK root class, add interceptors on api.client, because the root class shares one client with every tag. See sdk.