@kubb/plugin-fetch
@kubb/plugin-fetch turns each OpenAPI operation into a typed async function that calls your API with the global Fetch API. The path, query parameters, request body, response, and error shape all come from the spec, so a call stays in sync with the API it targets.
From your spec, the generated client gives you:
- Typed functions per operation with grouped
path,query,headers, andbody. - A status-keyed result on every call, or a thrown
ResponseError. - Auth resolved from your OpenAPI security schemes.
- Serialization of parameters and bodies across content types, including
multipart/form-datauploads and binary downloads. - Runtime validation against
@kubb/plugin-zodschemas. - Typed server-sent events you read with
for await. - Interceptors and a custom transport for the send.
- Standalone functions or a class-based SDK.
It sources its operation types from @kubb/plugin-ts, or from @kubb/plugin-zod with inferred: true when plugin-ts is absent. Add neither and generation stops with a warning. The client uses the built-in fetch, so there is no extra HTTP dependency to install.
Each function takes one grouped options object ({ path, query, headers, body }) and returns a RequestResult of { status, data, error, contentType, request, response }, bundled into .kubb/client.ts. See error handling for throwOnError and the status-keyed result union.
The bundled client also exposes getUrl, which builds an operation's final URL without sending the request, useful for cache keys, prefetch, and links:
import { client } from './.kubb/client'
const url = client.getUrl({ url: '/pet/{petId}', path: { petId: 1 }, query: { status: ['available'] } })
// '/pet/1?status=available'The runtime sets method, headers, body, signal, and credentials itself. To reach the rest of RequestInit (cache, mode, redirect, keepalive, duplex, or Next.js's next), pass options, on the client or per call, where a per-call value wins:
import { client } from './.kubb/client'
import { getPetById } from './getPetById'
client.setConfig({ options: { cache: 'no-store' } })
await getPetById({ path: { petId: 1 }, options: { cache: 'force-cache', next: { revalidate: 60 } } })For cross-cutting concerns like retries and interceptors, reach for a custom transport instead.
Installation
bun add -d @kubb/plugin-fetchpnpm add -D @kubb/plugin-fetchnpm install --save-dev @kubb/plugin-fetchyarn add -D @kubb/plugin-fetchDependencies
This plugin needs @kubb/plugin-ts, or @kubb/plugin-zod with inferred: true when plugin-ts is absent, for the operation types. plugin-ts wins when both are configured, and @kubb/plugin-zod is also required when validator is 'zod'.
IMPORTANT
The generated functions call the native fetch, so there is no HTTP client to install.
Example
import { defineConfig } from 'kubb'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginFetch } from '@kubb/plugin-fetch'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
plugins: [
pluginTs(),
pluginFetch({
output: { path: 'clients', mode: 'directory', barrel: { type: 'named' } },
baseURL: 'https://petstore.swagger.io/v2',
group: {
type: 'tag',
name: ({ group }) => `${group}Service`,
},
}),
],
})