Call operations
Generated Fetch and Axios operations accept grouped request options and return a typed RequestResult by default. Both clients share the calling convention below.
Call an operation
Pass parameters under path, query, headers, cookies, or body, matching the OpenAPI operation.
import { getPetById } from './gen/clients/getPetById'
const { data } = await getPetById({ path: { petId: 1 } })
// ^ the parsed pet, typed from the 200 response
Query, header, and cookie parameters sit under their own keys, and a request body goes under
body:
import { searchPets } from './gen/clients/searchPets'
import { updatePet } from './gen/clients/updatePet'
await searchPets({
query: { status: 'available', category: 'dogs', limit: 10, offset: 0 },
})
await updatePet({
path: { petId: '123' },
headers: { 'X-Request-ID': 'req-123456' },
body: { name: 'Updated name', status: 'sold' },
})
Each key is optional and only appears when the operation declares it, so an operation with no
parameters is called with an empty object, getStatus({}). How Kubb encodes arrays and objects in
each location is covered in serialization.
Read the result
A resolved call returns a RequestResult discriminated by the numeric status:
type RequestResult = {
status: number
data: TData // the parsed success body, undefined on an error result
error: TError // the parsed error body, undefined on a success result
contentType: string | undefined // the negotiated response media type
request: Request // the native request (AxiosRequestConfig on plugin-axios)
response: Response // the native response (AxiosResponse on plugin-axios)
}
When throwOnError is true (the generated default), a resolved call is always a success, so you
read data straight away:
const { data, status, response } = await getPetById({ path: { petId: 1 } })
console.info(status) // 200
console.info(response.headers.get('x-ratelimit-remaining'))
When an operation documents more than one success status, narrow on status to reach the body
for that case, and TypeScript follows the check:
const result = await getPetById({ path: { petId: 1 } })
if (result.status === 200) {
console.info(result.data.name)
}
Reading the error body and handling failures is covered in
error handling.
Unwrap the success body
Call .unwrap() to return the success body. Awaiting the operation directly returns the full result.
import { getPetById } from './gen/clients/getPetById'
const pet = await getPetById({ path: { petId: 1 } }).unwrap()
// ^ the parsed pet, not the full RequestResult
How unwrap() handles a failure depends on throwOnError. With throwOnError: true, a non-2xx
response already throws a ResponseError before the call resolves, so unwrap() throws the same
ResponseError a plain await would:
import { ResponseError } from './gen/.kubb/client'
try {
const pet = await getPetById({ path: { petId: 1 } }).unwrap()
} catch (error) {
if (ResponseError.is(error)) {
console.error(error.status) // 404
}
}
With throwOnError: false, .unwrap() rejects with the parsed error body instead of a ResponseError:
try {
const pet = await getPetById({ path: { petId: 1 }, throwOnError: false }).unwrap()
} catch (error) {
// the parsed error body, not a ResponseError
console.error(error)
}
Reading error off the full result instead of catching it is covered in
error handling.
Set the content type
When an operation accepts or returns more than one media type, set contentType on the call. A
bare string sets the request content type. The object form also sends an Accept header for the
response:
await uploadAvatar({
path: { petId: '123' },
body: avatarBlob,
contentType: 'image/png',
})
await getPet({
path: { petId: '123' },
contentType: { request: 'application/json', response: 'application/xml' },
})
For operations that already declare a single content type, Kubb bakes it into the generated function, so a multipart upload needs only the body:
import { uploadFile } from './gen/clients/uploadFile'
// the generated function already sets contentType: { request: 'multipart/form-data' }
await uploadFile({ path: { petId: '123' }, body: { file: pngBlob } })
How each content type maps to a request body, and how a response body is decoded, lives in serialization.
Reuse one configuration
Every generated function imports a shared client. Call setConfig once at startup and every
call picks up the change:
import { client } from './gen/.kubb/client'
client.setConfig({
baseURL: 'https://api.example.com/v1',
headers: { 'X-Client': 'web' },
})
Create and pass a separate client for isolated configuration:
import { createClient } from './gen/.kubb/client'
import { getPetById } from './gen/clients/getPetById'
const staging = createClient({ baseURL: 'https://staging.example.com/v1' })
await getPetById({ path: { petId: 1 }, client: staging })
The configuration object is the same ClientConfig in both cases.
Validate response bodies
Add @kubb/plugin-zod and set the Fetch client's validator to 'zod' to check each success and error response at runtime. A body that fails its generated schema throws a ParseError. See the validator reference for request validation and per-direction settings.
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginZod } from '@kubb/plugin-zod'
import { pluginFetch } from '@kubb/plugin-fetch'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen', clean: true },
plugins: [
pluginTs({ output: { path: 'types', mode: 'directory' } }),
pluginZod({ output: { path: 'zod', mode: 'directory' } }),
pluginFetch({ output: { path: 'clients', mode: 'directory' }, validator: 'zod' }),
],
})
The generated operation validates the response before returning its typed result:
import { findPetsByStatus } from './src/gen/clients/findPetsByStatus'
const { data } = await findPetsByStatus({ query: { status: ['available'] } })
Pass native client options
Pass options on an operation for native transport settings:
import { getPetById } from './gen/clients/getPetById'
// Axios client
await getPetById({ path: { petId: 1 }, options: { timeout: 5_000 } })
For Fetch clients, use options such as cache, mode, redirect, keepalive, duplex, or next. Axios supports timeout, proxy, maxRedirects, decompress, and onUploadProgress.
Set client.setConfig({ options }) for shared defaults. Per-call options take precedence. Kubb controls serialization and HTTP error handling, as described in custom transport.
Build a URL without sending
Use client.getUrl to build a URL with the same base URL, path interpolation, and query serialization as a request:
import { client } from './gen/.kubb/client'
const url = client.getUrl({
url: '/pets/{petId}',
path: { petId: 1 },
query: { fields: 'name' },
})
// https://api.example.com/v1/pets/1?fields=name