# Source: https://kubb.dev/ --- layout: page titleTemplate: false editLink: false --- ::home-page :: --- # About Source: https://kubb.dev/about --- layout: doc sidebar: false editLink: false title: About description: Learn about Kubb's origin, philosophy, and plugin-based architecture. Discover why it's the meta framework for code generation and why it's named after the Swedish lawn game Kubb. --- # About Kubb ## The Meta Framework for Code Generation Kubb is the meta framework for code generation. Like Next.js on top of React, Kubb is a meta framework on top of code generation. Your OpenAPI specification is the source of truth; Kubb provides the pipeline, [plugins](/docs/5.x/explanation/extensions#plugins), and extensibility you need. Every output format is an independent plugin. The core is intentionally minimal. Plugins compose. You bring your spec, configure the outputs you need, and Kubb generates everything in one `kubb generate` run: TypeScript types, API clients, React Query hooks, Zod schemas, MSW handlers, and Faker mocks. ## What does "Kubb" mean? Kubb is named after the popular Swedish lawn game [Kubb](https://en.wikipedia.org/wiki/Kubb). The name reflects the project's philosophy: just as the game Kubb uses wooden blocks (kubbar), our Kubb uses modular plugins as building blocks.
Kubb (pronounced [kubː] in Swedish and Gutnish) is a lawn game where the objective is to knock over wooden blocks (kubbar) by throwing wooden batons (kastpinnar) at them. Kubb can be described as a combination of bowling and horseshoes.The name is short, sounds like "cube" (fitting for a modular system), and happens to be Stijn's favorite summer game. ## Project philosophy Every feature in Kubb is implemented as a separate [plugin](/docs/5.x/explanation/extensions#plugins). This is what makes Kubb a _meta_ framework rather than a fixed code generator: there is no monolithic output, only composable pieces you assemble. ### Current focus Kubb currently specializes in generating TypeScript code from OpenAPI/Swagger specifications. To add support for a new specification format, you write a new [adapter](/docs/5.x/explanation/architecture#adapters). The entire plugin ecosystem works unchanged on top of it. ## Creator and maintainer Kubb is created and maintained by [Stijn Van Hulle](https://stijnvanhulle.be). Follow [@stijnvanhulle on BlueSky](https://bsky.app/profile/stijnvanhulle.bsky.social) for release announcements, new plugins, and development notes. ## Community and contributions Use [GitHub Discussions](https://github.com/kubb-labs/kubb/discussions) for questions, [Discord](https://discord.gg/shfBFeczrm) for real-time chat, and [GitHub Issues](https://github.com/kubb-labs/kubb/issues) for bugs and feature requests. Kubb is MIT-licensed and open to contributions. Bug fixes, new plugins, and documentation improvements are all welcome. See the [plugin guide](/docs/5.x/tutorials/creating-plugins) to build your own. ## Support the project If Kubb saves you time, consider [sponsoring on GitHub](https://github.com/sponsors/stijnvanhulle). --- # Adapters - Explore the Ecosystem Source: https://kubb.dev/adapters --- layout: page title: Adapters - Explore the Ecosystem description: Discover official and community adapters for Kubb. Parse OpenAPI, GraphQL, and other specifications into Kubb's universal Abstract Syntax Tree. editLink: false --- ::adapters-page :: --- # Blog - OpenAPI Code Generator Updates & Tutorials Source: https://kubb.dev/blog --- title: Blog - OpenAPI Code Generator Updates & Tutorials description: Latest updates, release notes, and tutorials for Kubb - the TypeScript code generator for OpenAPI specifications. Learn about new features and best practices. layout: page sidebar: false editLink: false search: false --- ::blogs --- blogs: - title: Release of Kubb 5.0 href: /blog/v5/ cover: /blog/v5/cover.svg detail: "A ground-up rewrite around a layered pipeline: an adapter parses your spec into a universal AST, plugins walk it, parsers emit the code. Generation runs up to 5x faster than v4." date: 20 Aug 2026 tag: release - title: Kubb as a Claude Code plugin href: /blog/claude-code-plugin/ cover: /blog/claude-code-plugin/cover.svg detail: Install Kubb inside Claude Code and turn an OpenAPI spec into TypeScript types, Zod schemas, clients and React Query hooks. Ships slash commands, a config skill, an expert agent and the Kubb MCP server. date: 29 May 2026 tag: plugin - title: Release of Kubb 4.0 href: /blog/v4/ cover: /blog/v4/cover.svg detail: Correct use of Infinite Query generics with Tanstack-Query, unwrap in Vue infinite query, different use of z.optional, and add Key suffix to types when using asConst. date: 22 Sep 2025 tag: release - title: Release of Kubb 3.0 href: /blog/v3/ cover: /blog/v3/cover.svg detail: Updated plugin architecture with progress bar, dropped Tanstack Query v4 support, per-framework packages, improved generator system, and extended OpenAPI v3/v3.1 support. date: 1 Sep 2024 tag: release --- :: --- # Contributors - OpenAPI Generator Community Source: https://kubb.dev/contributors --- layout: page title: Contributors - OpenAPI Generator Community description: Meet the developers and contributors building Kubb, the TypeScript code generator for OpenAPI specifications. Join our open-source community. editLink: false --- ::contributors-page :: --- # Ecosystem - Projects, Articles & Community Source: https://kubb.dev/ecosystem --- layout: doc title: Ecosystem - Projects, Articles & Community description: Discover projects built with Kubb, articles featuring Kubb, and community resources. See how teams use Kubb for OpenAPI code generation. outline: deep editLink: false --- # Kubb Ecosystem Projects and articles from the community. ## Production projects - [KOPER](https://github.com/dxloop/koper) - full-stack application framework using Kubb for type-safe API integration - [Xata MCP Server](https://xata.io/blog/built-xata-mcp-server) - Xata's MCP server built with Kubb's OpenAPI-to-MCP generation - [ClickHouse + MooseStack](https://clickhouse.com/blog/clickhouse-powered-apis-in-react-app-moosestack#bridging-to-the-frontend) - ClickHouse-powered APIs in React apps using Kubb - [Fiveonefour](https://docs.fiveonefour.com/moose/apis/openapi-sdk) - OpenAPI SDK generation powered by Kubb ## Articles and tutorials - [API Consumption with OpenAPI and Typed Code](https://www.rocketseat.com.br/blog/artigos/post/consumo-apis-openapi-codigo-tipado) (RocketSeat) - front-end API consumption using OpenAPI and Kubb - [OpenAPI to Frontend](https://apisyouwonthate.com/newsletter/openapi-to-frontend/) (APIs You Won't Hate) - using OpenAPI specs for frontend development - [Kubb Nedir? React Query ve Zod ile Swagger'dan Otomatik Kod Üretimi](https://mfkdev.medium.com/kubb-nedir-react-query-ve-zod-ile-swaggerdan-otomatik-kod-%C3%BCretimi-28b8ad756d80) (Medium) - Turkish article about generating React Query and Zod code from Swagger with Kubb - [Securing Auth0 API Calls in Next.js](https://medium.com/@laxman.pokhrel.101/from-headache-to-harmony-securing-auth0-api-calls-in-next-js-54fc117bfb92) (Medium) - Kubb, Axios, and Next.js App Router - [Which OpenAPI Codegen Should You Choose? openapi-typescript vs Hey API vs Orval vs Kubb](https://dev.to/nyaomaru/which-openapi-codegen-should-you-choose-openapi-typescript-vs-hey-api-vs-orval-vs-kubb-100p) (DEV Community) - comparison of OpenAPI code generators, including Kubb ## Community discussions - [Heyapi or openapi-ts with FastAPI and Tanstack Query?](https://www.reddit.com/r/reactjs/comments/1o9vcfy/heyapi_or_openapits_with_fastapi_and_tanstack/) (Reddit r/reactjs) - community comparison of OpenAPI tooling options ## Listed in - [TanStack Query Community Projects](https://tanstack.com/query/v5/docs/framework/react/community/community-projects) - [Zod Ecosystem](https://zod.dev/ecosystem?id=x-to-zod) - "X to Zod" tools ## Community Join the [Discord](https://discord.gg/shfBFeczrm) for questions and discussion. Using Kubb in a project? Share it on [GitHub Discussions](https://github.com/kubb-labs/kubb/discussions) and we can add it here. --- # Parsers - Explore the Ecosystem Source: https://kubb.dev/parsers --- layout: page title: Parsers - Explore the Ecosystem description: Discover official and community parsers for Kubb. Convert Kubb's universal AST into TypeScript, JavaScript and other source files. editLink: false --- ::parsers-page :: --- # Plugins - Explore the Ecosystem Source: https://kubb.dev/plugins --- layout: page title: Plugins - Explore the Ecosystem description: Discover official and community plugins for Kubb. Generate TypeScript types, TanStack Query hooks, Zod schemas, MSW mocks, and more from your OpenAPI specification. editLink: false --- ::plugins-page :: --- # Sponsors Source: https://kubb.dev/sponsors --- layout: page title: Sponsors description: Support Kubb's development and help us build better OpenAPI code generation tools. Become a sponsor and get your name featured. editLink: false --- ::sponsors-page :: --- # Kubb OpenAPI Adapter Source: https://kubb.dev/adapters/adapter-oas --- layout: doc title: Kubb OpenAPI Adapter description: Reads an OpenAPI 2.0, 3.0, or 3.1 spec and converts every schema and operation into the AST that Kubb plugins generate from, so one adapter feeds the whole build. outline: deep kind: adapter id: adapter-oas name: OpenAPI category: openapi type: official npmPackage: "@kubb/adapter-oas" repo: https://github.com/kubb-labs/kubb docsPath: /adapters/adapter-oas featured: true icon: light: https://kubb.dev/feature/openapi.svg maintainers: - name: Stijn Van Hulle github: stijnvanhulle compatibility: kubb: ">=5.0.0" node: ">=22" tags: - openapi - swagger - api-spec - parser - converter resources: documentation: https://kubb.dev/adapters/adapter-oas repository: https://github.com/kubb-labs/kubb issues: https://github.com/kubb-labs/kubb/issues changelog: https://github.com/kubb-labs/kubb/blob/main/packages/adapter-oas/CHANGELOG.md --- # @kubb/adapter-oas `@kubb/adapter-oas` reads and validates OpenAPI 2.0, 3.0, and 3.1 documents, then converts schemas and operations into the AST used by every plugin. Configure it on `defineConfig.adapter`. Its type mappings apply to every plugin in the build. Kubb uses `adapterOas()` by default. Add it explicitly to change [Options](./reference/options). ## Installation ::code-group{sync="package-manager"} ```shell [bun] bun add -d @kubb/adapter-oas ``` ```shell [pnpm] pnpm add -D @kubb/adapter-oas ``` ```shell [npm] npm install --save-dev @kubb/adapter-oas ``` ```shell [yarn] yarn add -D @kubb/adapter-oas ``` :: ## Dependencies No plugin dependencies. ## Example ```typescript twoslash [kubb.config.ts] import { defineConfig } from 'kubb' import { adapterOas } from '@kubb/adapter-oas' import { pluginTs } from '@kubb/plugin-ts' export default defineConfig({ input: './petStore.yaml', output: { path: './src/gen' }, adapter: adapterOas({ dateType: 'date', integerType: 'number' }), plugins: [pluginTs()], }) ``` ## See also - [Changelog](https://github.com/kubb-labs/kubb/blob/main/packages/adapter-oas/CHANGELOG.md) --- # Options Source: https://kubb.dev/adapters/adapter-oas/reference/options --- layout: doc title: Options description: Configuration options for @kubb/adapter-oas covering spec validation, content type, the server base URL, discriminators, enums, and how OpenAPI types map to TypeScript. outline: deep --- # Options All `adapterOas` options are optional. Types and defaults are listed below. | Option | Type | Default | Description | | ------ | ---- | ------- | ----------- | | [`validate`](#validate) | `boolean` | `true` | Validate the spec before parsing | | [`contentType`](#contenttype) | `'application/json' \| string` | — | Preferred media type for request and response schemas | | [`server`](#server) | `{ index?: number, variables?: Record
`: `listPets200Schema` becomes `listPetsStatus200Schema`.
- Output uses chained Zod 4 calls. Functional wrappers remain for `mini: true`. Getters are used only for circular references.
See [Zod options](/plugins/plugin-zod/reference/options).
## React Query {#plugin-react-query}
Register an Axios or Fetch client plugin. `client` is now `'axios' | 'fetch'`, auto-detected when exactly one client is registered. Move `baseURL` and validation to that client. Remove the query plugin's `parser`.
Set `hooks: true` to keep `use*` hooks. The default `false` still emits keys and option factories.
Queries and mutations use grouped `*Options`. The trailing client config excludes `path`, `query`, `body`, `headers`, and `url`. `TData` now contains only 2xx responses. Errors use `TError`. Remove imports of `*MutationKey` type aliases and use the runtime key helper.
The generated auto `enabled` guard is removed. Set TanStack Query's `enabled` or `skipToken` when deferring a request. Suspense hooks always run. See [React Query options](/plugins/plugin-react-query/reference/options).
## Vue Query {#plugin-vue-query}
Apply the React Query changes above. Each parameter group accepts `MaybeRefOrGetter`, and generated client calls unwrap it with `toValue()`. Set `hooks: true` to retain composables. See [Vue Query options](/plugins/plugin-vue-query/reference/options).
## SWR {#plugin-swr}
Use the same client selector, grouped parameters, and client-level validation. Remove `mutation.paramsToTrigger`. Mutation parameters always pass through `trigger()`:
```typescript [src/app.ts]
useUpdatePet().trigger({ path: { petId }, body: pet })
```
SWR drops the parameter-presence guard and keys requests off `shouldFetch`. Set it to `false` to disable a request. See [SWR options](/plugins/plugin-swr/reference/options).
## Faker {#plugin-faker}
Remove `paramsCasing` and `mapper`. Use macros or printers, keeping mock values compatible with the TypeScript output. `createPet` keeps its name but accepts generic `TData`, preserving the types of override fields in its return value. See [Faker options](/plugins/plugin-faker/reference/options).
## MSW {#plugin-msw}
The `parser`, `handlers`, and `baseURL` options retain their v4 behavior. Generated handlers use `HttpResponseResolver` typed against request bodies and headers. Apply the shared resolver, adapter `contentType`, and generator-option migrations. See [MSW options](/plugins/plugin-msw/reference/options).
## Cypress {#plugin-cypress}
Request helpers take grouped `*Options` followed by the unchanged `Partial`. Remove `dataReturnType`. Helpers yield the body as `Cypress.Chainable`. HTTP methods are uppercase and imports use `*Options`/`*Response` names. `baseURL`, `exclude`, `include`, and `override` retain their shape. There is no validator option. See [Cypress options](/plugins/plugin-cypress/reference/options).
## MCP {#plugin-mcp}
Register TypeScript, Zod, and an Axios or Fetch client. `client` selects the registered client. Move `baseURL` there and remove `paramsCasing`. Handlers receive grouped `*Options` plus `RequestHandlerExtra`, delegate to generated client operations, and read `res.data`. See [MCP options](/plugins/plugin-mcp/reference/options).
## Verify the upgrade {#verify-the-upgrade}
Run `kubb generate`, review generated-file changes, and type-check your application. Update response imports for `Status` names, Zod inferred-type imports for `Type`, and imports from renamed tag folders. Enable formatting, linting, and barrels explicitly if needed.
The default banner is controlled by root `output.defaultBanner`. Per-plugin `output.banner` and `output.footer` accept strings or per-file functions. Operations with multiple request content types generate per-content-type types plus a union and accept a typed `contentType` argument.
## See also
- [Configuration](/docs/5.x/reference/configuration)
- [Quickstart](/docs/5.x/tutorials/quickstart)
- [Plugin catalogue](/plugins)
---
# Customize generated code with printers
Source: https://kubb.dev/docs/5.x/how-to/printers
---
layout: doc
title: Customize generated code with printers
description: Printers turn Kubb's AST schema nodes into generated code. The
printer option on plugin-ts, plugin-zod, and plugin-faker replaces the handler
for a single schema type, so you change what one plugin emits without forking
it.
outline: deep
order: 5
navigation:
title: Customize generated code with printers
icon: i-iconoir-printing-page
---
# Customize generated code with printers
Set `printer.nodes` on TypeScript, Zod, or Faker plugins to change how schema types are emitted. Override only the handlers you need.
For callback signatures and context properties, see the [AST reference](/docs/5.x/reference/kit/ast#printers).
## TypeScript types
`@kubb/plugin-ts` builds TypeScript AST nodes, so a handler returns a `ts.TypeNode` created with the compiler's factory. This override prints `date` schemas as the JavaScript `Date` object instead of `string`.
```typescript [date.ts]
import ts from 'typescript'
import { pluginTs } from '@kubb/plugin-ts'
pluginTs({
printer: {
nodes: {
date() {
return ts.factory.createTypeReferenceNode('Date', [])
},
},
},
})
```
## Zod schemas
Write regular methods when you use `this.base`, `this.transform`, or `this.options`; arrow functions do not receive the printer context.
`@kubb/plugin-zod` prints expression strings, so a handler returns the Zod code as a string. With `mini: true` the same overrides target the Zod Mini printer instead.
```typescript twoslash [date.ts]
import { pluginZod } from '@kubb/plugin-zod'
pluginZod({
printer: {
nodes: {
date() {
return 'z.iso.date()'
},
},
},
})
```
Use `this.base` to keep the default output and decorate it. This override appends `.openapi(...)` to every object schema, and nested nodes still print through the regular handlers.
```typescript twoslash [openapi.ts]
import { pluginZod } from '@kubb/plugin-zod'
pluginZod({
printer: {
nodes: {
object(node) {
return `${this.base(node)}.openapi(${JSON.stringify({ description: node.description })})`
},
},
},
})
```
A Zod handler can also read `this.options.direction`, which is `'decode'` while printing response schemas and `'encode'` while printing request bodies and parameters. Return a different expression per direction and the plugin treats the node as a two-way conversion, emitting an `${name}InputSchema` variant that request bodies resolve to. See [Encode a custom type on requests](/plugins/plugin-zod/guide/customization#encode-custom-types).
## Faker mocks
`@kubb/plugin-faker` also prints strings, one Faker expression per schema node. This override generates floats where the spec declares integers.
```typescript twoslash [integer.ts]
import { pluginFaker } from '@kubb/plugin-faker'
pluginFaker({
printer: {
nodes: {
integer() {
return 'faker.number.float()'
},
},
},
})
```
## Choose a customization
Use a printer for output-specific code such as `Date` or `.openapi(...)`. Use a [macro](/docs/5.x/how-to/macros) to change schema nodes before printing, or a [resolver](/docs/5.x/how-to/resolvers) to change names and paths. Macros run before printer handlers.
---
# Configure generation
Source: https://kubb.dev/docs/5.x/how-to/recipes
---
layout: doc
title: Configure generation
description: Configure common Kubb outputs, multiple specifications, watch mode,
formatters, and programmatic builds.
outline:
- 2
- 3
order: 1
navigation:
title: Configure generation
icon: i-iconoir-flask
---
# Configure generation
Use these configurations in an existing project with [Kubb installed](/docs/5.x/installation) and an OpenAPI specification. Replace `./petStore.yaml` with your specification path, install the packages for your chosen stack, then run `npx kubb generate`.
The examples write to `./src/gen`. With `clean: true`, Kubb removes that directory before generation, so keep handwritten files elsewhere. See [Configuration](/docs/5.x/reference/configuration) for option defaults.
## TypeScript only
The smallest setup, generating TypeScript types and interfaces from your OpenAPI spec.
```shell [Terminal]
npm install -D kubb typescript @kubb/plugin-ts
```
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen', clean: true },
plugins: [pluginTs()],
})
```
## TypeScript + React Query
Generates types and [TanStack Query](https://tanstack.com/query) hooks for React.
```shell [Terminal]
npm install -D kubb typescript @kubb/plugin-ts @kubb/plugin-axios @kubb/plugin-react-query
npm install axios react @tanstack/react-query
```
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginAxios } from '@kubb/plugin-axios'
import { pluginReactQuery } from '@kubb/plugin-react-query'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen', clean: true },
plugins: [pluginTs(), pluginAxios(), pluginReactQuery({ hooks: true })],
})
```
## TypeScript + Vue Query
Generates types and [TanStack Query](https://tanstack.com/query) hooks for Vue.
```shell [Terminal]
npm install -D kubb typescript @kubb/plugin-ts @kubb/plugin-axios @kubb/plugin-vue-query
npm install axios vue @tanstack/vue-query
```
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginVueQuery } from '@kubb/plugin-vue-query'
import { pluginAxios } from '@kubb/plugin-axios'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen', clean: true },
plugins: [pluginTs(), pluginAxios(), pluginVueQuery({ hooks: true })],
})
```
## Zod schemas + MSW handlers
Runtime validation with [Zod](https://zod.dev), plus [MSW](https://mswjs.io) request handlers backed by [Faker.js](https://fakerjs.dev) mock data.
```shell [Terminal]
npm install -D kubb typescript @kubb/plugin-ts @kubb/plugin-zod @kubb/plugin-faker @kubb/plugin-msw
npm install zod msw @faker-js/faker
```
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginZod } from '@kubb/plugin-zod'
import { pluginFaker } from '@kubb/plugin-faker'
import { pluginMsw } from '@kubb/plugin-msw'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen', clean: true },
plugins: [pluginTs(), pluginZod(), pluginFaker(), pluginMsw({ parser: 'faker', handlers: true })],
})
```
## Pick the HTTP client
Choose [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) or [Axios](https://axios-http.com) by registering the matching plugin. Replace `pluginAxios` with `pluginFetch` to use global `fetch`, and change the import to `@kubb/plugin-fetch`. Install `@kubb/plugin-fetch` as a development dependency; the generated client uses the environment’s global `fetch`.
```shell [Terminal]
npm install -D kubb typescript @kubb/plugin-ts @kubb/plugin-axios
npm install axios
```
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginAxios } from '@kubb/plugin-axios'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen', clean: true },
plugins: [pluginTs(), pluginAxios()],
})
```
## Multiple specifications
Generate from several specs in one run. Pass an array to [`defineConfig`](/docs/5.x/reference/configuration). Each entry runs on its own, with its own plugins and output directory.
Set a `name` per entry so each one shows up in the CLI output.
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
export default defineConfig([
{
name: 'petStore',
input: './petStore.yaml',
output: { path: './src/gen/petStore', clean: true },
plugins: [pluginTs()],
},
{
name: 'userApi',
input: './userApi.yaml',
output: { path: './src/gen/userApi', clean: true },
plugins: [pluginTs()],
},
])
```
## Conditional config (watch-aware)
Pass a function to [`defineConfig`](/docs/5.x/reference/configuration) to read CLI context. Here it turns off `clean` in watch mode so incremental runs stay fast.
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
export default defineConfig(({ watch }) => ({
input: './petStore.yaml',
output: {
path: './src/gen',
clean: !watch,
},
plugins: [pluginTs()],
}))
```
Run `kubb generate --watch` to regenerate on spec changes.
## Format with Biome, lint with Oxlint
Format generated files with [Biome](https://biomejs.dev) and lint them with [Oxlint](https://oxc.rs/docs/guide/usage/linter) on every build. Set `format` and `lint` to `'auto'` to pick whichever tool is installed.
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
export default defineConfig({
input: './petStore.yaml',
output: {
path: './src/gen',
clean: true,
format: 'biome',
lint: 'oxlint',
},
plugins: [pluginTs()],
})
```
Install the tools as development dependencies with `npm install -D @biomejs/biome oxlint` before running this configuration.
## Run a command after generation
Install the command you plan to run in your project. For the example below, use `npm install -D @biomejs/biome`.
Use [`output.postGenerate`](/docs/5.x/reference/configuration#output-postgenerate) to run shell commands, such as a formatter pass or a type check, once the generated files are formatted and linted.
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
export default defineConfig({
input: './petStore.yaml',
output: {
path: './src/gen',
postGenerate: ['biome check --write ./src/gen'],
},
plugins: [pluginTs()],
})
```
## Programmatic build
Drive Kubb from a script with [`createKubb`](/docs/5.x/reference/kit/engine#createkubb) from the `kubb` package, paired with `Diagnostics` from `kubb/kit`. This fits monorepo orchestration and custom build pipelines. It applies the same defaults as `defineConfig`, so a shared config generates the same files through the CLI and a script.
```typescript twoslash [generate.ts]
import { createKubb } from 'kubb'
import { Diagnostics } from 'kubb/kit'
import { pluginTs } from '@kubb/plugin-ts'
const kubb = createKubb({
input: './petStore.yaml',
output: { path: './gen' },
plugins: [pluginTs()],
})
kubb.hooks.hook('kubb:plugin:end', ({ plugin, duration }) => {
console.log(`${plugin.name} completed in ${duration}ms`)
})
const { files, diagnostics } = await kubb.safeBuild()
if (Diagnostics.hasError(diagnostics)) {
for (const diagnostic of diagnostics.filter(Diagnostics.isProblem)) {
if (diagnostic.severity === 'error') {
console.error(`${diagnostic.plugin ?? 'kubb'}: ${diagnostic.message}`)
}
}
process.exit(1)
}
console.log(`Generated ${files.length} files`)
```
Use `.build()` instead of `.safeBuild()` if you want it to throw on errors rather than return `diagnostics`. See the [Kit API](/docs/5.x/reference/kit/engine#createkubb) for the full `Kubb` instance API.
## Validate in CI
Run `kubb validate ./petStore.yaml` before generation to fail on an invalid specification. See [Validate command](/docs/5.x/reference/commands/validate) and [CI snapshots](/docs/5.x/integrations/ci).
---
# Customize names and paths
Source: https://kubb.dev/docs/5.x/how-to/resolvers
---
layout: doc
title: Customize names and paths
description: Resolvers name every symbol and file Kubb generates. The resolver
option on each code-generating plugin patches a single naming rule, so you
change what things are called or where files land without forking the plugin.
outline: deep
order: 2
navigation:
title: Customize names and paths
icon: i-iconoir-label
---
# Customize names and paths
Set a plugin’s `resolver` option to change generated identifiers and file paths. Supply only the methods you need to replace. Other methods keep their defaults.
For callback signatures and context properties, see the [Resolver reference](/docs/5.x/reference/kit/resolvers#resolver-overrides).
## Rename identifiers
Prefix TypeScript names while preserving the plugin’s PascalCase preset:
```typescript twoslash [prefix.ts]
import { pluginTs, resolverTs } from '@kubb/plugin-ts'
pluginTs({
resolver: {
name(name) {
return `Api${resolverTs.name(name)}`
},
},
})
```
## Rename and relocate files
`file.baseName` builds a file's name, extension included. Here it renames every Faker file to `.mock.ts` instead of the plugin default.
```typescript twoslash [file-name.ts]
import { pluginFaker } from '@kubb/plugin-faker'
pluginFaker({
resolver: {
file: {
baseName({ name, extname }) {
return `${name}.mock${extname}`
},
},
},
})
```
`file.path` returns the file's full path and bypasses the `output.path` and `group` layout, so the resolver owns where the file lands. This override moves every Faker file into a `mocks/` folder. The returned path may not escape the project root.
```typescript twoslash [file-path.ts]
import { pluginFaker } from '@kubb/plugin-faker'
pluginFaker({
resolver: {
file: {
path({ baseName, output }) {
return `${output.path}/mocks/${baseName}`
},
},
},
})
```
## Namespaced names
Use regular methods when accessing `this`. Within a namespace, `this.name` applies the active naming rule; calling it from the top-level `name` override would recurse. Wrap the exported plugin resolver there instead.
Override `query.keyName` to rename React Query keys. Use `this.name` to retain the active naming rule:
```typescript twoslash [query-key.ts]
import { pluginReactQuery } from '@kubb/plugin-react-query'
pluginReactQuery({
resolver: {
query: {
keyName(node) {
return `${this.name(node.operationId)}Key`
},
},
},
})
```
`@kubb/plugin-ts` names each response type through `response.status`. This override rewrites the template so a `200` response reads `GetPetById200Response` instead of the default `GetPetByIdStatus200`.
```typescript twoslash [response.ts]
import { pluginTs } from '@kubb/plugin-ts'
pluginTs({
resolver: {
response: {
status(node, statusCode) {
return this.name(`${node.operationId} ${statusCode} response`)
},
},
},
})
```
---
# Installation
Source: https://kubb.dev/docs/5.x/installation
---
layout: doc
title: Installation
description: Install Kubb, verify the CLI, and choose output plugins for your project.
outline: [2, 3]
order: 1
navigation:
title: Installation
icon: i-iconoir-download
---
# Installation
Install Kubb in an existing project using your package manager. If you want to learn generation with a supplied specification and expected output, follow [Generate your first client](/docs/5.x/tutorials/quickstart).
## Prerequisites
- [Node.js](https://nodejs.org/) 22 or higher.
- TypeScript 5 or higher if you use a TypeScript config or import generated types.
::steps{level="2"}
## Install Kubb
Add `kubb` as a development dependency. It includes the CLI, core runtime, default OpenAPI adapter, and TypeScript, TSX, and Markdown parsers.
::code-group
```shell [pnpm]
pnpm add -D kubb
```
```shell [npm]
npm install -D kubb
```
```shell [yarn]
yarn add -D kubb
```
```shell [bun]
bun add -d kubb
```
::
## Verify the installation
Run the CLI from your project directory:
```shell [Terminal]
npx kubb --version
```
The command prints the installed Kubb version. Use `pnpm exec kubb --version`, `yarn kubb --version`, or `bunx kubb --version` with the corresponding package manager.
## Install plugins
Each output is provided by a plugin package. Install only the plugins you need. For TypeScript types, install the TypeScript plugin and compiler:
::code-group
```shell [pnpm]
pnpm add -D @kubb/plugin-ts typescript
```
```shell [npm]
npm install -D @kubb/plugin-ts typescript
```
```shell [yarn]
yarn add -D @kubb/plugin-ts typescript
```
```shell [bun]
bun add -d @kubb/plugin-ts typescript
```
::
Browse [available plugins](/plugins) to choose outputs for your project.
## Configure your project
Run the [initialization wizard](/docs/5.x/reference/commands/init) to choose your specification, output directory, and plugins:
```shell [Terminal]
npx kubb init
```
For an existing config, use [Configure generation](/docs/5.x/how-to/recipes). Install the runtime libraries required by your chosen output too: an Axios client uses `axios`, and React Query hooks use React and `@tanstack/react-query`. Each plugin documents its requirements.
Once configured, run `npx kubb generate` from the project directory. Kubb writes the generated files to `output.path`.
::
## Next steps
- [Generate your first client](/docs/5.x/tutorials/quickstart) — follow a complete example with a supplied specification.
- [Configure generation](/docs/5.x/how-to/recipes) — choose a stack for your existing project.
- [Configuration reference](/docs/5.x/reference/configuration) — look up options and defaults.
---
# Generate with Astro
Source: https://kubb.dev/docs/5.x/integrations/astro
---
layout: doc
title: Generate with Astro
description: Run Kubb code generation during Astro builds.
outline: [2, 3]
order: 12
navigation:
title: Astro
icon: i-simple-icons-astro
---
# Generate with Astro
The integration uses `unplugin-kubb`, which ships with `kubb`.
> [!IMPORTANT]
> This integration generates during builds only. Run `kubb generate` before starting the development server.
::steps{level="2"}
## Install Kubb and your output plugins
Follow the [installation guide](/docs/5.x/installation) to add Kubb and the plugins your output needs.
## Configure the integration
Import your shared `kubb.config.ts` and pass it to the integration:
```typescript [astro.config.mjs]
import { defineConfig } from 'astro/config'
import kubb from 'kubb/astro'
import config from './kubb.config'
export default defineConfig({
integrations: [kubb({ config })],
})
```
Run your project's Astro build command to generate the files. Bundler integrations do not run `output.postGenerate` commands; use the CLI when you need them.
::
## See also
- [Configuration](/docs/5.x/reference/configuration)
- [Other build tools](/docs/5.x/integrations/build-tools)
---
# Build tool integrations
Source: https://kubb.dev/docs/5.x/integrations/build-tools
---
layout: doc
title: Build tool integrations
description: Choose a Kubb integration for your build tool.
---
# Build tool integrations
Kubb runs generation during your build through `unplugin-kubb`, included with `kubb`. Each integration accepts your shared `kubb.config.ts`.
- [Vite](/docs/5.x/integrations/vite)
- [webpack](/docs/5.x/integrations/webpack)
- [Rollup](/docs/5.x/integrations/rollup)
- [Rolldown](/docs/5.x/integrations/rolldown)
- [Rspack](/docs/5.x/integrations/rspack)
- [esbuild](/docs/5.x/integrations/esbuild)
- [Farm](/docs/5.x/integrations/farm)
- [Nuxt](/docs/5.x/integrations/nuxt)
- [Astro](/docs/5.x/integrations/astro)
---
# Publish snapshots from CI
Source: https://kubb.dev/docs/5.x/integrations/ci
---
layout: doc
title: Publish snapshots from CI
description: Publish installable Kubb Studio snapshots from GitHub Actions or
GitLab CI and review generated-file changes.
outline:
- 2
- 3
---
# Publish snapshots from CI
`kubb studio snapshot` generates a package on a CI runner, publishes it to [Studio](/docs/5.x/integrations/studio), and exits. Run snapshots on pull or merge requests and the default branch so reviewers can compare their output.
## Set the token
Create an organization CI API key in Studio and store it as `KUBB_TOKEN`. On GitHub, use an Actions secret. On GitLab, use a masked CI/CD variable. Snapshot installation uses a separate `registry` API key.
Snapshots expire after seven days. Schedule a default-branch run if it can go a week without a push.
## GitHub Actions
Follow [Publish snapshots with GitHub Actions](/docs/5.x/integrations/github-actions) for a workflow that publishes snapshots and comments on pull requests. The [action reference](/docs/5.x/reference/github-actions) lists its inputs and outputs.
## GitLab CI
Follow [Publish snapshots with GitLab CI](/docs/5.x/integrations/gitlab) for the job and an optional script that posts a merge request note.
## Other CI providers
For a provider without automatic agent identification, use a stable `--id` on the base branch and pass it as `--base-id` on pull request runs:
```shell [Terminal]
kubb studio snapshot --id jenkins:api:main # on main
kubb studio snapshot --id jenkins:api:pr-12 --base-id jenkins:api:main # on a pull request
```
Use `kubb studio snapshot --json | jq -r '.url'` to pass the tarball URL to another step. See [CI agent identity](/docs/5.x/reference/commands/studio#ci-agent-identity) for automatic provider detection and [Snapshot output](/docs/5.x/reference/commands/studio#snapshot-output) for the result fields.
## Install the snapshot
Configure the registry API key, then run the install command from the snapshot result:
```ini [.npmrc]
//kubb.studio/:_authToken=${KUBB_REGISTRY_TOKEN}
```
```shell [Terminal]
npm i https://kubb.studio/packages//.tgz
```
## See also
- [Studio setup](/docs/5.x/integrations/studio)
- [Studio command reference](/docs/5.x/reference/commands/studio)
---
# Generate with esbuild
Source: https://kubb.dev/docs/5.x/integrations/esbuild
---
layout: doc
title: Generate with esbuild
description: Run Kubb code generation during esbuild builds.
outline: [2, 3]
order: 9
navigation:
title: esbuild
icon: i-simple-icons-esbuild
---
# Generate with esbuild
The integration uses `unplugin-kubb`, which ships with `kubb`.
::steps{level="2"}
## Install Kubb and your output plugins
Follow the [installation guide](/docs/5.x/installation) to add Kubb and the plugins your output needs.
## Configure the integration
Import your shared `kubb.config.ts` and pass it to the integration:
```typescript [build.ts]
import { build } from 'esbuild'
import kubb from 'kubb/esbuild'
import config from './kubb.config'
await build({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/bundle.js',
plugins: [kubb({ config })],
})
```
Run your project's esbuild build command to generate the files. Bundler integrations do not run `output.postGenerate` commands; use the CLI when you need them.
::
## See also
- [Configuration](/docs/5.x/reference/configuration)
- [Other build tools](/docs/5.x/integrations/build-tools)
---
# Generate with Farm
Source: https://kubb.dev/docs/5.x/integrations/farm
---
layout: doc
title: Generate with Farm
description: Run Kubb code generation during Farm builds.
outline: [2, 3]
order: 10
navigation:
title: Farm
icon: i-iconoir-light-bulb
---
# Generate with Farm
The integration uses `unplugin-kubb`, which ships with `kubb`.
::steps{level="2"}
## Install Kubb and your output plugins
Follow the [installation guide](/docs/5.x/installation) to add Kubb and the plugins your output needs.
## Configure the integration
Import your shared `kubb.config.ts` and pass it to the integration:
```typescript [farm.config.ts]
import { defineConfig } from '@farmfe/core'
import kubb from 'kubb/farm'
import config from './kubb.config'
export default defineConfig({
plugins: [kubb({ config })],
})
```
Run your project's Farm build command to generate the files. Bundler integrations do not run `output.postGenerate` commands; use the CLI when you need them.
::
## See also
- [Configuration](/docs/5.x/reference/configuration)
- [Other build tools](/docs/5.x/integrations/build-tools)
---
# GitHub Actions
Source: https://kubb.dev/docs/5.x/integrations/github-actions
---
layout: doc
title: GitHub Actions
description: Publish Kubb Studio snapshots from GitHub Actions and review generated-file changes.
order: 2
navigation:
title: GitHub Actions
icon: i-simple-icons-githubactions
---
# GitHub Actions
Publish an installable Kubb Studio snapshot from GitHub Actions so reviewers can compare generated files against the base branch and previous runs.
## Prerequisites
- A repository with a working `kubb.config.ts`. Follow [Installation](/docs/5.x/installation) if you have not set up Kubb.
- A [Kubb Studio](/docs/5.x/integrations/studio) organization and an organization CI API key.
Store the CI API key as `KUBB_TOKEN` in an Actions repository secret. Snapshot installation uses a separate registry key. Only expose the CI secret to trusted pipelines.
## Configure the workflow
Add this workflow:
```yaml [.github/workflows/kubb.yml]
name: Kubb snapshot
on:
pull_request:
# A snapshot of main is what pull requests compare with.
push:
branches: [main]
permissions:
contents: write
pull-requests: write
# Runs of one pull request or branch share a Studio agent, so run them one at a time.
concurrency:
group: kubb-snapshot-${{ github.ref }}
cancel-in-progress: true
jobs:
snapshot:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: kubb-labs/action@v1
with:
token: ${{ secrets.KUBB_TOKEN }}
```
`pull-requests: write` allows snapshot comments. `contents: write` lets the action open an initialization pull request when the repository has no Kubb config.
Review the pull request comment for generated changes against the base branch and the previous run.
To pass the tarball URL to another step, give the action an `id: snapshot`, then read ${{ steps.snapshot.outputs.tarball-url }}. See the [action reference](/docs/5.x/reference/github-actions) for all inputs, outputs, and runtime behavior.
## Review and install the result
Open the snapshot in Studio to review generated changes. Snapshots expire after seven days; schedule a default-branch run if it can go a week without a push.
To consume the generated package, follow [Install the snapshot](/docs/5.x/integrations/ci#install-the-snapshot).
## See also
- [Studio command reference](/docs/5.x/reference/commands/studio)
- [Publish snapshots from other CI providers](/docs/5.x/integrations/ci#other-ci-providers)
- [GitHub Actions inputs and outputs](/docs/5.x/reference/github-actions)
---
# GitLab CI
Source: https://kubb.dev/docs/5.x/integrations/gitlab
---
layout: doc
title: GitLab CI
description: Publish Kubb Studio snapshots from GitLab CI and review generated-file changes.
order: 3
navigation:
title: GitLab CI
icon: i-simple-icons-gitlab
---
# GitLab CI
Publish an installable Kubb Studio snapshot from GitLab CI so reviewers can compare generated files against the base branch and previous runs.
## Prerequisites
- A repository with a working `kubb.config.ts`. Follow [Installation](/docs/5.x/installation) if you have not set up Kubb.
- A [Kubb Studio](/docs/5.x/integrations/studio) organization and an organization CI API key.
- Commit your npm lockfile and add `kubb` to the project’s development dependencies so `npm ci` installs the CLI.
Store the CI API key as `KUBB_TOKEN` in a masked CI/CD variable. Snapshot installation uses a separate registry key. Only expose the CI secret to trusted pipelines.
## Configure the workflow
Add this job:
```yaml [.gitlab-ci.yml]
snapshot:
stage: build
image: node:22
resource_group: kubb-snapshot-$CI_COMMIT_REF_SLUG
script:
- npm ci
- npx kubb studio snapshot
rules:
- if: $CI_MERGE_REQUEST_IID
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
```
`kubb` ships the Studio runtime, so `npm ci` is the only setup. The `rules` run the job on merge request pipelines and on the default branch, so a merge request has a snapshot of its target branch to compare with.
`resource_group` keeps two pipelines on the same branch or merge request from running the job at once. Registering the agent again ends the other run's session.
### Post the result as a note
Use `--json` to capture the snapshot result, then post it as a merge request note with this script. It updates the same note on each pipeline and uses only Node:
```js [scripts/kubb-note.mjs]
import { readFileSync } from 'node:fs'
const snapshot = JSON.parse(readFileSync('snapshot.json', 'utf8'))
const marker = ''
const counts = (c) => `${c.added.length} added · ${c.changed.length} changed · ${c.removed.length} removed`
const lines = [marker, `Kubb snapshot: \`npm i ${snapshot.url}\``]
const { branchChanges: branch, changes } = snapshot
if (branch) {
if (branch.base) lines.push(`Changes against \`${branch.branch}\`: ${counts(branch)}`)
else if (branch.baseFound) lines.push(`No snapshot of \`${branch.branch}\` for this package yet`)
else lines.push(`No snapshot of \`${branch.branch}\` to compare with`)
}
if (changes?.base) lines.push(`Changes since ${changes.base.commit?.slice(0, 7) ?? changes.base.createdAt}: ${counts(changes)}`)
const { CI_API_V4_URL, CI_PROJECT_ID, CI_MERGE_REQUEST_IID, GITLAB_API_TOKEN } = process.env
const notes = `${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}/notes`
const headers = { 'PRIVATE-TOKEN': GITLAB_API_TOKEN, 'Content-Type': 'application/json' }
const existing = (await (await fetch(`${notes}?per_page=100`, { headers })).json()).find((note) => note.body.startsWith(marker))
const response = await fetch(existing ? `${notes}/${existing.id}` : notes, {
method: existing ? 'PUT' : 'POST',
headers,
body: JSON.stringify({ body: lines.join('\n\n') }),
})
if (!response.ok) throw new Error(`GitLab answered ${response.status}`)
```
```yaml [.gitlab-ci.yml]
script:
- npm ci
- npx kubb studio snapshot --json > snapshot.json
- '[ -z "$CI_MERGE_REQUEST_IID" ] || node scripts/kubb-note.mjs'
```
Writing a note needs a project access token with the `api` scope, stored as `GITLAB_API_TOKEN`. `CI_JOB_TOKEN` does not carry it.
## Review and install the result
Open the snapshot in Studio to review generated changes. Snapshots expire after seven days; schedule a default-branch run if it can go a week without a push.
To consume the generated package, follow [Install the snapshot](/docs/5.x/integrations/ci#install-the-snapshot).
## See also
- [Studio command reference](/docs/5.x/reference/commands/studio)
- [Publish snapshots from other CI providers](/docs/5.x/integrations/ci#other-ci-providers)
---
# Generate with Nuxt
Source: https://kubb.dev/docs/5.x/integrations/nuxt
---
layout: doc
title: Generate with Nuxt
description: Run Kubb code generation during Nuxt builds.
outline: [2, 3]
order: 11
navigation:
title: Nuxt
icon: i-simple-icons-nuxt
---
# Generate with Nuxt
The integration uses `unplugin-kubb`, which ships with `kubb`.
> [!IMPORTANT]
> This integration generates during builds only. Run `kubb generate` before starting the development server.
::steps{level="2"}
## Install Kubb and your output plugins
Follow the [installation guide](/docs/5.x/installation) to add Kubb and the plugins your output needs.
## Configure the integration
Import your shared `kubb.config.ts` and pass it to the integration:
```typescript [nuxt.config.ts]
import config from './kubb.config'
export default defineNuxtConfig({
modules: [['kubb/nuxt', { config }]],
})
```
Run your project's Nuxt build command to generate the files. Bundler integrations do not run `output.postGenerate` commands; use the CLI when you need them.
::
## See also
- [Configuration](/docs/5.x/reference/configuration)
- [Other build tools](/docs/5.x/integrations/build-tools)
---
# Generate with Rolldown
Source: https://kubb.dev/docs/5.x/integrations/rolldown
---
layout: doc
title: Generate with Rolldown
description: Run Kubb code generation during Rolldown builds.
outline: [2, 3]
order: 7
navigation:
title: Rolldown
icon: i-simple-icons-rolldown
---
# Generate with Rolldown
The integration uses `unplugin-kubb`, which ships with `kubb`.
::steps{level="2"}
## Install Kubb and your output plugins
Follow the [installation guide](/docs/5.x/installation) to add Kubb and the plugins your output needs.
## Configure the integration
Import your shared `kubb.config.ts` and pass it to the integration:
```typescript [rolldown.config.ts]
import kubb from 'kubb/rolldown'
import { defineConfig } from 'rolldown'
import config from './kubb.config'
export default defineConfig({
input: 'src/index.ts',
plugins: [kubb({ config })],
})
```
Run your project's Rolldown build command to generate the files. Bundler integrations do not run `output.postGenerate` commands; use the CLI when you need them.
::
## See also
- [Configuration](/docs/5.x/reference/configuration)
- [Other build tools](/docs/5.x/integrations/build-tools)
---
# Generate with Rollup
Source: https://kubb.dev/docs/5.x/integrations/rollup
---
layout: doc
title: Generate with Rollup
description: Run Kubb code generation during Rollup builds.
outline: [2, 3]
order: 6
navigation:
title: Rollup
icon: i-simple-icons-rollupdotjs
---
# Generate with Rollup
The integration uses `unplugin-kubb`, which ships with `kubb`.
::steps{level="2"}
## Install Kubb and your output plugins
Follow the [installation guide](/docs/5.x/installation) to add Kubb and the plugins your output needs.
## Configure the integration
Import your shared `kubb.config.ts` and pass it to the integration:
```typescript [rollup.config.ts]
import kubb from 'kubb/rollup'
import config from './kubb.config'
export default {
input: 'src/index.ts',
plugins: [kubb({ config })],
}
```
Run your project's Rollup build command to generate the files. Bundler integrations do not run `output.postGenerate` commands; use the CLI when you need them.
::
## See also
- [Configuration](/docs/5.x/reference/configuration)
- [Other build tools](/docs/5.x/integrations/build-tools)
---
# Generate with Rspack
Source: https://kubb.dev/docs/5.x/integrations/rspack
---
layout: doc
title: Generate with Rspack
description: Run Kubb code generation during Rspack builds.
outline: [2, 3]
order: 8
navigation:
title: Rspack
icon: i-iconoir-journal
---
# Generate with Rspack
The integration uses `unplugin-kubb`, which ships with `kubb`.
::steps{level="2"}
## Install Kubb and your output plugins
Follow the [installation guide](/docs/5.x/installation) to add Kubb and the plugins your output needs.
## Configure the integration
Import your shared `kubb.config.ts` and pass it to the integration:
```typescript [rspack.config.ts]
import kubb from 'kubb/rspack'
import config from './kubb.config'
export default {
plugins: [kubb({ config })],
}
```
Run your project's Rspack build command to generate the files. Bundler integrations do not run `output.postGenerate` commands; use the CLI when you need them.
::
## See also
- [Configuration](/docs/5.x/reference/configuration)
- [Other build tools](/docs/5.x/integrations/build-tools)
---
# Connect a project to Studio
Source: https://kubb.dev/docs/5.x/integrations/studio
---
layout: doc
title: Connect a project to Studio
description: Connect a local Kubb project to Studio, review generated files, and keep an agent running in the background.
outline: [2, 3]
order: 1
navigation:
title: Kubb Studio
icon: i-iconoir-cube
---
# Connect a project to Studio
Use [Kubb Studio](https://kubb.studio) to generate and review a project's clients, types, schemas, hooks, and mocks from the browser. Start with Kubb installed and a `kubb.config.ts` in your project.
For a quick trial without local setup, use Studio's shared sandbox with a spec you are comfortable providing outside your environment. For a persistent team connection on your infrastructure, use the [Docker agent](https://hub.docker.com/r/kubblabs/kubb-agent).
## Connect your project
Run this command from the project directory:
```shell [Terminal]
kubb studio
```
Approve the machine in the Studio window and answer the permission prompts. Keep the command running, select the connected agent in Studio, and generate to see progress and the file tree.
With a CLI or Docker agent, generation runs in your environment. Studio receives plugin settings, progress, and generated file paths. A local spec is not uploaded. Reading generated file contents or changing local files requires the agent's permission.
## Review and save generated files
To view file contents and diffs, then write generated files to disk, connect with:
```shell [Terminal]
kubb studio --allow-read --allow-write
```
With no permissions granted, generation uses memory and Studio sees file paths and progress. It does not write files or edit your config. The CLI remembers permission answers per project.
Add `--allow-config-edit` to save plugin changes to `kubb.config.ts`. Add `--allow-exec` to run the configured formatter, linter, and `output.postGenerate` commands. See the [command reference](/docs/5.x/reference/commands/studio#options) for all permission flags.
## Keep an agent running in the background
Start the connection from your project directory:
```shell [Terminal]
kubb studio start --allow-read
```
Complete pairing and permission prompts if this is the first connection. Check that the worker is connected:
```shell [Terminal]
kubb studio status
```
To end the background connection, run:
```shell [Terminal]
kubb studio stop
```
Stop the worker before opening a foreground connection. Restart it after a crash or reboot. If status reports `authentication required`, run `kubb studio login`, then start again. See [Background connection](/docs/5.x/reference/commands/studio#background-connection) for worker states, logs, and restart behavior.
In CI or without a TTY, pass an existing `KUBB_AGENT_TOKEN` and the permission flags the run needs. Headless runs do not prompt.
## Snapshot from CI
Follow [Publish snapshots from CI](/docs/5.x/integrations/ci) to produce installable packages and compare generated-file changes on pull or merge requests. Snapshot generation uses an organization CI API key. Installation uses a separate `registry` API key.
## See also
- [`kubb studio` command](/docs/5.x/reference/commands/studio): actions, flags, environment variables, and snapshot results
- [Configuration](/docs/5.x/reference/configuration): the config a connected project uses
---
# Generate with Vite
Source: https://kubb.dev/docs/5.x/integrations/vite
---
layout: doc
title: Generate with Vite
description: Run Kubb code generation during Vite builds.
outline: [2, 3]
order: 4
navigation:
title: Vite
icon: i-simple-icons-vite
---
# Generate with Vite
The integration uses `unplugin-kubb`, which ships with `kubb`.
> [!IMPORTANT]
> This integration generates during builds only. Run `kubb generate` before starting the development server.
::steps{level="2"}
## Install Kubb and your output plugins
Follow the [installation guide](/docs/5.x/installation) to add Kubb and the plugins your output needs.
## Configure the integration
Import your shared `kubb.config.ts` and pass it to the integration:
```typescript [vite.config.ts]
import kubb from 'kubb/vite'
import { defineConfig } from 'vite'
import config from './kubb.config'
export default defineConfig({
plugins: [kubb({ config })],
})
```
Run your project's Vite build command to generate the files. Bundler integrations do not run `output.postGenerate` commands; use the CLI when you need them.
::
## See also
- [Configuration](/docs/5.x/reference/configuration)
- [Other build tools](/docs/5.x/integrations/build-tools)
---
# Generate with webpack
Source: https://kubb.dev/docs/5.x/integrations/webpack
---
layout: doc
title: Generate with webpack
description: Run Kubb code generation during webpack builds.
outline: [2, 3]
order: 5
navigation:
title: webpack
icon: i-simple-icons-webpack
---
# Generate with webpack
The integration uses `unplugin-kubb`, which ships with `kubb`.
::steps{level="2"}
## Install Kubb and your output plugins
Follow the [installation guide](/docs/5.x/installation) to add Kubb and the plugins your output needs.
## Configure the integration
Import your shared `kubb.config.ts` and pass it to the integration:
```typescript [webpack.config.ts]
import kubb from 'kubb/webpack'
import config from './kubb.config'
export default {
plugins: [kubb({ config })],
}
```
Run your project's webpack build command to generate the files. Bundler integrations do not run `output.postGenerate` commands; use the CLI when you need them.
::
## See also
- [Configuration](/docs/5.x/reference/configuration)
- [Other build tools](/docs/5.x/integrations/build-tools)
---
# Introduction
Source: https://kubb.dev/docs/5.x/introduction
---
layout: doc
title: Introduction
description: Generate types, API clients, query hooks, validators, and mocks
from API specifications with Kubb.
outline:
- 2
- 3
order: 0
navigation:
title: Introduction
icon: i-iconoir-info-circle
---
# Introduction
Kubb generates code from API specifications: TypeScript types, API clients, query hooks, validators, and mocks. Regenerate when your specification changes to keep these files in sync with your API, then import them in your application.
Choose the outputs you need with plugins in one `kubb.config.ts` file. Generated files belong to your project. The default [OpenAPI adapter](/adapters/adapter-oas/) supports OpenAPI 2.0, 3.0, and 3.1.
## Features
- Generate [TypeScript types](/plugins/plugin-ts/), typed [Axios](/plugins/plugin-axios/) or [Fetch](/plugins/plugin-fetch/) clients, [React Query](/plugins/plugin-react-query/), [Vue Query](/plugins/plugin-vue-query/), and [SWR](/plugins/plugin-swr/) hooks.
- Validate data with [Zod](/plugins/plugin-zod/), create [Faker](/plugins/plugin-faker/) data and [MSW](/plugins/plugin-msw/) handlers, generate [Cypress](/plugins/plugin-cypress/) tests, or expose your API through a generated [MCP server](/plugins/plugin-mcp/).
- Customize the pipeline with adapters, plugins, parsers, macros, and resolvers, or write output to disk, memory, or a custom storage backend.
- Run generation with [Vite and webpack](/docs/5.x/integrations/build-tools), other bundlers, or in CI. Connect AI tools through [MCP](/docs/5.x/ai/mcp) or use the [Claude Code integration](/docs/5.x/ai/claude).
## See it work
The animation below follows a spec through Kubb's generation pipeline. The adapter turns schemas and operations into AST nodes, then plugins generate files from those nodes.
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginZod } from '@kubb/plugin-zod'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
plugins: [pluginTs(), pluginZod()],
})
```
::spec-journey
::
See the [architecture guide](/docs/5.x/explanation/architecture) for details about each stage.
## Start here
| You want to | Read |
| --- | --- |
| Install Kubb in a project | [Installation](/docs/5.x/installation) |
| Generate your first client | [Generate your first client](/docs/5.x/tutorials/quickstart) |
| Configure a stack or workflow | [Configure generation](/docs/5.x/how-to/recipes) |
| Rename files or customize generated code | [Resolvers](/docs/5.x/how-to/resolvers), [macros](/docs/5.x/how-to/macros), and [printers](/docs/5.x/how-to/printers) |
| Run generation during a build or in CI | [Vite](/docs/5.x/integrations/vite), [webpack](/docs/5.x/integrations/webpack), and [CI](/docs/5.x/integrations/ci) |
| Use Kubb with a browser or AI assistant | [Studio](/docs/5.x/integrations/studio), [MCP](/docs/5.x/ai/mcp), and [Claude Code](/docs/5.x/ai/claude) |
| Build a plugin | [Plugin tutorial](/docs/5.x/tutorials/creating-plugins) |
| Look up an option or API | [Configuration](/docs/5.x/reference/configuration), [commands](/docs/5.x/reference/commands/), and [Kit API](/docs/5.x/reference/kit) |
| Understand the pipeline | [Architecture](/docs/5.x/explanation/architecture) |
| Upgrade from v4 | [Migration guide](/docs/5.x/how-to/migration) |
## Community
Kubb is free and open source under the MIT license. Get help on [Discord](https://discord.gg/shfBFeczrm), report an issue on [GitHub](https://github.com/kubb-labs/kubb/issues), or read [Contributing](/docs/5.x/community/contributing).
---
# Commands
Source: https://kubb.dev/docs/5.x/reference/commands
---
layout: doc
title: Commands
description: Reference for every command and flag exposed by the kubb CLI including init, generate, validate, mcp and studio.
outline: [2, 3]
navigation:
title: Commands
icon: i-iconoir-terminal
---
# Commands
The `kubb` CLI is the main way to run Kubb. It reads your [configuration](/docs/5.x/reference/configuration) and runs the generation pipeline. It also scaffolds projects, validates specs, starts a Model Context Protocol server for LLM clients, and connects a project to Kubb Studio.
## Usage
```text [Terminal]
USAGE kubb [COMMAND] [OPTIONS]
COMMANDS
init Initialize a new Kubb project with interactive setup
generate [input] Generate files based on a 'kubb.config.ts' file (default)
validate Validate a Swagger/OpenAPI file
mcp Start the MCP server so an MCP client can interact with the LLM
studio [action] Connect this project to Kubb Studio and generate from the browser
Use kubb --help for more information about a command.
```
Run `kubb` with no command and it runs `kubb generate`.
## Available commands
| Command | Description |
| ----------------------------- | ----------------------------------------------------------------- |
| [`kubb init`](./init) | Scaffold a new Kubb project with an interactive wizard. |
| [`kubb generate`](./generate) | Run the code-generation pipeline from your `kubb.config.ts`. |
| [`kubb validate`](./validate) | Validate a Swagger/OpenAPI document without running the pipeline. |
| [`kubb mcp`](./mcp) | Start a Model Context Protocol server for LLM clients. |
| [`kubb studio`](./studio) | Connect the project to Kubb Studio and generate from the browser. |
## Environment variables
The CLI reads these shared environment variables.
| Variable | Type | Used by | Description |
| ------------------------ | --------- | -------- | --------------------------------------------------------------------------------- |
| `KUBB_DISABLE_TELEMETRY` | `boolean` | all | Turn off anonymous usage telemetry. Set it to `1` or `true`. |
| `DO_NOT_TRACK` | `boolean` | all | Standard opt-out convention. Set it to `1` or `true`. |
| `KUBB_HOME` | `string` | `studio` | Directory the CLI keeps its Studio state in. Defaults to `~/.kubb`. |
| `KUBB_AGENT_TOKEN` | `string` | `studio` | Connect with an existing agent token instead of approving this machine. |
---
# kubb generate
Source: https://kubb.dev/docs/5.x/reference/commands/generate
---
layout: doc
title: kubb generate
description: The generate command runs the Kubb code-generation pipeline based
on your kubb.config.ts.
outline:
- 2
- 3
order: 2
navigation:
title: kubb generate
icon: i-iconoir-play
---
# kubb generate
Run `kubb generate` to read your [`kubb.config.ts`](/docs/5.x/reference/configuration) and run the code-generation pipeline. It's the default command, so running `kubb` with no arguments does the same.
## Usage
Generate from the input set in your config:
```shell [Terminal]
kubb generate
```
Pass an input file to override the config:
```shell [Terminal]
kubb generate ./petStore.yaml
```
## Arguments
| Argument | Description |
| --------- | -------------------------------------------------------------------------------------------- |
| `[input]` | Optional path or URL to a Swagger/OpenAPI document. Overrides the `input` set in the config. |
## Options
| Option | Default | Description |
| -------------------------------------------- | ------- | ------------------------------------------------------------------------- |
| `--config=`, `-c ` | | Path to a config file, such as `./kubb.staging.ts`. |
| `--log-level=`, `-l` | `info` | Set the verbosity. Use `verbose` to see plugin timings. |
| `--silent`, `-s` | `false` | Force the log level to `silent`. Suppresses output. |
| `--verbose` | `false` | Force the log level to `verbose`. Shows slow plugins. |
| `--reporter=` | | Pick which reporters to trigger, comma-separated. Defaults to `cli`. |
| `--watch`, `-w` | `false` | Re-run the pipeline whenever the input spec changes. |
| `--dry-run` | `false` | Preview a run without writing files, formatting, linting, or running post-generate commands. |
`--reporter` takes no short flag.
## Reporters
A reporter decides how a run is rendered. The config registers available reporters with [`reporters`](/docs/5.x/reference/configuration). `--reporter` picks which ones to trigger by name, comma-separated (`--reporter cli,file`). Four reporters are registered by default.
| Reporter | Output |
| -------- | ------------------------------------------------------------------------------- |
| `cli` | The end-of-run summary in the terminal. Default. Uses plain text in non-interactive terminals and AI agent sessions. |
| `json` | A machine-readable report on stdout for CI. See [Diagnostics](/docs/5.x/reference/diagnostics#machine-readable-output) for the full JSON shape. |
| `file` | The run's diagnostics, written to `.kubb/kubb[-]-.log`. The `` segment is dropped when the config has no `name`. |
| `html` | A browsable report directory with generated files and diagnostics, written to `.kubb/kubb[-]-/index.html`. |
Write a log file:
```shell [Terminal]
kubb generate --reporter file
```
Print a JSON report for CI. The exit code is non-zero on any error:
```shell [Terminal]
kubb generate --reporter json
```
## See also
- [Configuration](/docs/5.x/reference/configuration): full reference for `kubb.config.ts`
- [Basic usage](/docs/5.x/tutorials/quickstart): end-to-end walkthrough
- [Plugins](/plugins): available plugins for code generation
---
# kubb init
Source: https://kubb.dev/docs/5.x/reference/commands/init
---
layout: doc
title: kubb init
description: The init command bootstraps a fresh Kubb project with an interactive wizard.
outline:
- 2
- 3
order: 1
navigation:
title: kubb init
icon: i-iconoir-magic-wand
---
# kubb init
Run `kubb init` for an interactive setup wizard. Answer a few questions and Kubb creates a `package.json` if one is missing, installs your chosen plugins, and writes a ready-to-use `kubb.config.ts`.
## Usage
Run the command in the directory where your Kubb project lives:
```shell [Terminal]
npx kubb init
```
The wizard prompts for three things:
- The path to your OpenAPI/Swagger spec, a local file or a URL.
- The output directory for generated files.
- Which [plugins](/plugins) to install, such as clients, hooks, validators, and mocks.
Kubb detects the package manager (`bun`, `pnpm`, `npm`, or `yarn`) from your project, so it does not prompt for one.
The wizard writes the config and installs `kubb` with your selected plugins.
The wizard installs `kubb` at the exact version of the CLI you ran and takes the plugins from that same release channel, so `npx kubb@beta init` scaffolds a beta project and a stable CLI scaffolds a stable one.
## Options
| Option | Default | Description |
| ---------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--yes`, `-y` | `false` | Skip all prompts and use the default values. |
| `--input`, `-i` | | Path to the OpenAPI specification (local file or URL). Bypasses the spec path prompt. |
| `--output`, `-o` | | Output directory for generated files. Bypasses the output directory prompt. |
| `--plugins` | | Comma-separated list of plugins to install. Bypasses the plugin selection prompt. Valid values: `plugin-ts`, `plugin-axios`, `plugin-fetch`, `plugin-react-query`, `plugin-vue-query`, `plugin-zod`, `plugin-faker`, `plugin-msw`, `plugin-cypress`, `plugin-mcp`, `plugin-redoc`. |
| `--dry-run` | `false` | Preview the wizard without installing packages or writing `kubb.config.ts`. |
Each flag skips only its own prompt and combines with `--yes`. Pass all three value flags and the wizard runs without prompts, no `--yes` needed.
## Examples
Run with the defaults and no prompts:
```shell [Terminal]
npx kubb init --yes
```
Run with no prompts and a specific spec, output directory, and plugins:
```shell [Terminal]
npx kubb init --input ./openapi.yaml --output ./src/gen --plugins plugin-ts,plugin-zod
```
Preview what the wizard would install and write, without changing anything:
```shell [Terminal]
npx kubb init --yes --dry-run
```
## See also
- [Installation](/docs/5.x/installation): manual setup guide
- [Configuration](/docs/5.x/reference/configuration): full `kubb.config.ts` reference
- [Plugins](/plugins): browse available plugins
---
# kubb mcp
Source: https://kubb.dev/docs/5.x/reference/commands/mcp
---
layout: doc
title: kubb mcp
description: The mcp command starts a Model Context Protocol server so LLM
clients can interact with your schemas and trigger generation.
outline:
- 2
- 3
order: 4
navigation:
title: kubb mcp
icon: i-simple-icons-modelcontextprotocol
---
# kubb mcp
Run `kubb mcp` to start a [Model Context Protocol](https://modelcontextprotocol.io/) server. LLM clients such as Claude, Cursor, and VS Code can then read your schemas and trigger generation.
> [!WARNING]
> This feature is under active development. Use it with caution and expect breaking changes.
::terminal
---
command: kubb mcp
output:
- ⏳ Starting MCP server...
- This feature is still under development, use with caution
---
::
## Usage
Start the MCP server over stdio, the transport every major LLM client speaks:
```shell [Terminal]
kubb mcp
```
## Tools
The MCP server exposes three tools to connected clients.
| Tool | Description |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `generate` | Runs the Kubb pipeline against a resolved `kubb.config.ts` and streams log messages back to the client. |
| `validate` | Validates an OpenAPI or Swagger document at a path or URL. Needs `@kubb/adapter-oas` installed in the project. |
| `init` | Scaffolds a `kubb.config.ts` in the current directory without prompts. It does not install packages. |
## Example
Most MCP clients accept a JSON config with `command` and `args`. Register the Kubb MCP server over stdio:
```json [mcp.json]
{
"mcpServers": {
"kubb": {
"command": "npx",
"args": ["kubb", "mcp"]
}
}
}
```
## See also
- [MCP integration guide](/docs/5.x/ai/mcp): connect the server to Claude Desktop, Cursor, and other clients
- [`@kubb/plugin-mcp`](/plugins/plugin-mcp/), a different package that generates an MCP server from your OpenAPI spec
- [Concepts: Plugins](/docs/5.x/explanation/extensions#plugins): how plugins integrate with the Kubb pipeline
---
# kubb studio
Source: https://kubb.dev/docs/5.x/reference/commands/studio
---
layout: doc
title: kubb studio
description: Command reference for connecting a project to Kubb Studio, managing
pairing and permissions, and publishing CI snapshots.
outline:
- 2
- 3
order: 5
navigation:
title: kubb studio
icon: i-iconoir-cube
---
# kubb studio
Run `kubb studio` to connect a project to [Kubb Studio](https://kubb.studio). The CLI runs generation on your machine and sends progress and generated file paths to Studio. Grant `--allow-read` to view generated file contents and diffs in the browser.
## Usage
Connect the current project. On the first run, approve the pairing code in Studio. The CLI then asks which permissions to grant for this project.
```shell [Terminal]
kubb studio
```
## Actions
The positional argument selects the action. It defaults to `connect`.
| Action | Description |
| ---------- | ----------------------------------------------------------------------- |
| `connect` | Hold a foreground session open and generate on request. |
| `start` | Resolve pairing and permissions, then connect this project in the background. |
| `stop` | Stop the background worker and keep its pairing. |
| `login` | Pair this machine without opening a generation session. |
| `logout` | Stop the background worker and forget this project's stored token. |
| `status` | Show worker state, log path, pairing, and saved permissions. |
| `snapshot` | Generate and publish a snapshot from a script, then exit. See [Publish snapshots from CI](/docs/5.x/integrations/ci). |
## Background connection
Run `start`, `status`, and `stop` from the same project directory. `start` resolves pairing and permission prompts before detaching, then returns while the worker connects.
`status` reports `starting`, `connected`, `reconnecting`, `authentication required`, or `stopped`. Only one worker can own a project. Stop it before using foreground `connect`.
The worker survives terminal closure and retries temporary connection failures. Restart it after a crash or reboot. If its token is rejected, run `kubb studio login`, then `kubb studio start`. To change its URL, config, or permissions, stop it and start with the new flags.
Credentials, worker state, and a bounded 1 MiB `worker.log` live under `$KUBB_HOME/projects//`. `KUBB_HOME` defaults to `~/.kubb`. `status` prints the log path. `stop` waits for active generation cleanup. `logout` stops the worker before removing credentials. Interrupted jobs are not replayed.
## Options
| Option | Default | Description |
| ------------------------------------------ | --------------------- | ---------------------------------------------------------------------------------- |
| `--config=`, `-c ` | | Path to a config file, such as `./kubb.staging.ts`. |
| `--url=` | `https://kubb.studio` | URL for Kubb Studio. |
| `--allow-read` | `false` | Let Studio read generated file contents. Asked once per project when omitted. |
| `--allow-write` | `false` | Write generated files to disk. Asked once per project when omitted. |
| `--allow-config-edit` | `false` | Let Studio change plugin options in `kubb.config.ts`. Asked once per project. |
| `--allow-exec` | `false` | Run the formatter, the linter, and `output.postGenerate`. Asked once per project. |
| `--no-open` | | Do not open the approval page in a browser. |
| `--log-level=`, `-l`| `info` | Set the verbosity. |
| `--token=` | | `snapshot` only: organization CI API key. Defaults to `KUBB_TOKEN`. |
| `--id=` | | `snapshot` only: stable identity for the CI agent. Auto-detected on GitHub Actions, GitLab CI, Bitbucket Pipelines and CircleCI. |
| `--base-id=` | | `snapshot` only: the `--id` of another CI agent, such as the base branch's, to also compare with. Auto-detected on GitHub pull requests and GitLab merge requests. |
| `--name=` | | `snapshot` only: package name for the tarball. Defaults to the name in `package.json`. |
| `--package-version=` | | `snapshot` only: package version for the tarball. Defaults to the version in `package.json`. |
| `--timeout=` | `600` | `snapshot` only: seconds to wait for the job to finish, capped at `3600`. |
| `--json` | `false` | `snapshot` only: print the result as one JSON object instead of a summary. |
Without `--allow-read`, a session still generates and reports file paths, but Studio cannot display their contents. The CLI asks about permissions not supplied as flags and remembers answers per project directory. In CI or without a TTY, it does not prompt, so pass the permissions the run needs explicitly.
## Environment variables
| Variable | Description |
| ------------------ | ---------------------------------------------------------------------------------- |
| `KUBB_HOME` | Directory the CLI keeps its Studio state in. Defaults to `~/.kubb`. |
| `KUBB_AGENT_TOKEN` | Connect with an existing agent token instead of approving this machine. |
| `KUBB_TOKEN` | `snapshot` only: organization CI API key. Same as `--token`. |
## Snapshot output
`--json` prints one JSON object with `id`, `name`, `version`, `integrity`, `url`, `snapshotIdUrl`, `expiresAt`, and `agentUrl`. It also includes `changes` and `branchChanges` when comparisons are available. Installing a snapshot requires a separate `registry` API key.
| JSON field | Compared with |
| --- | --- |
| `changes` | The previous snapshot on the same pull request or branch. |
| `branchChanges` | The latest snapshot of a GitHub pull request's base branch or a GitLab merge request's target branch. Its `baseFound` field distinguishes a branch with no CI agent from one with an agent that has no snapshot of this package yet. |
## CI agent identity
`snapshot` detects GitHub Actions, GitLab CI, Bitbucket Pipelines, and CircleCI. Other providers require a stable `--id`. Use a stable `--id` on the base branch and pass that identity as `--base-id` on pull request runs.
### GitLab CI
The CI agent registers as `gl::`, read from `CI_PROJECT_ID` and `CI_MERGE_REQUEST_IID`, so every pipeline on that merge request reuses one agent. A branch pipeline falls back to `CI_COMMIT_REF_SLUG`, so the default branch has one agent too. Pass `--id` to group runs your own way.
A merge request pipeline also names its target branch's agent, from `CI_MERGE_REQUEST_TARGET_BRANCH_NAME`. The snapshot reports which generated files changed against that branch's latest snapshot in `branchChanges`, and against the merge request's previous pipeline in `changes`. The commit it records is the source branch's head, `CI_MERGE_REQUEST_SOURCE_BRANCH_SHA`, when GitLab sets it.
## Examples
```shell [Terminal]
kubb studio # connect and answer permission prompts
kubb studio --allow-read # allow Studio to show generated file contents
kubb studio --allow-write --allow-exec # write files, run the formatter and linter
kubb studio start --allow-read # connect in the background
kubb studio status # show worker state and log path
kubb studio stop # stop while keeping pairing
kubb studio login # pair without opening a session
kubb studio logout # forget the stored token
kubb studio snapshot # generate and publish a snapshot from CI
kubb studio snapshot --json # print the snapshot as one JSON object
```
## See also
- [Kubb Studio guide](/docs/5.x/integrations/studio): connect a project and run headless
- [Commands](/docs/5.x/reference/commands/): every command the CLI exposes
- [Configuration](/docs/5.x/reference/configuration): the `kubb.config.ts` Studio reads
---
# kubb validate
Source: https://kubb.dev/docs/5.x/reference/commands/validate
---
layout: doc
title: kubb validate
description: The validate command checks that a Swagger/OpenAPI document is
valid without running the full pipeline.
outline:
- 2
- 3
order: 3
navigation:
title: kubb validate
icon: i-iconoir-check-circle
---
# kubb validate
Run `kubb validate` to check that a Swagger/OpenAPI document is valid without running the pipeline. Use it to catch errors early in CI or before you commit a spec change.
::terminal
---
command: kubb validate ./petStore.yaml
output:
- ✅ Validation success
---
::
## Usage
Validate a local OpenAPI file:
```shell [Terminal]
kubb validate ./petStore.yaml
```
Validate a remote document:
```shell [Terminal]
kubb validate https://petstore3.swagger.io/api/v3/openapi.json
```
## Arguments
| Argument | Required | Description |
| --------- | -------- | -------------------------------------------------------- |
| `` | `true` | Path or URL to the Swagger/OpenAPI document to validate. |
> [!TIP]
> `kubb validate` exits with a non-zero status code when the spec is invalid. Use it as a pre-commit hook or CI step to fail the build.
## See also
- [Adapters](/adapters): OAS adapter that parses the validated spec
- [Basic usage](/docs/5.x/tutorials/quickstart): end-to-end walkthrough
---
# Configuration
Source: https://kubb.dev/docs/5.x/reference/configuration
---
layout: doc
title: Configuration
description: Reference for kubb.config.ts with every option, default and example
for the Kubb v5 UserConfig.
outline:
- 2
- 3
order: 1
navigation:
title: Configuration
icon: i-iconoir-settings
---
# Configuration
`kubb.config.ts` drives a Kubb run. The file default-exports a `defineConfig` call. Pass it an object, a function that returns one, or an array of configs.
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
export default defineConfig({
name: 'petStore',
input: './petStore.yaml',
output: { path: './src/gen' },
})
```
> [!TIP]
> `defineConfig` from the `kubb` package adds the OpenAPI adapter and the TypeScript parsers for you, so you don't import them yourself.
## Config formats
### Single config object
Export an object as in the example above. `defineConfig` also accepts a Promise of a config.
### Config function
Pass a function when the config depends on the run context, such as `watch` or `logLevel`:
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
export default defineConfig(({ watch, logLevel }) => ({
name: 'petStore',
input: './petStore.yaml',
output: { path: './src/gen', clean: !watch },
}))
```
The function receives this context:
| | Type | Description |
| ----------: | :--- | :---------- |
| `input` | `string` | Positional input from `kubb generate `. Overrides `config.input` when set. |
| `watch` | `boolean` | `true` in watch mode. |
| `logLevel` | `'silent' \| 'info' \| 'verbose'` | Current log level. |
| `config` | `string` | Path to the config file in use. |
| `reporters` | `Array` | Reporters selected via `--reporter`, overriding `config.reporters`. |
### Multiple configurations (array)
Pass an array to generate from several specs in one command:
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
export default defineConfig([
{
name: 'petStore',
input: './petStore.yaml',
output: { path: './src/gen/petStore' },
plugins: [pluginTs()],
},
{
name: 'stripe',
input: './stripe.yaml',
output: { path: './src/gen/stripe' },
plugins: [pluginTs()],
},
])
```
A config function can return an array to combine both forms.
## Top-level options
### `name`
A name for this config. The CLI prints it as `Generating ...`.
| | |
| --------: | :------- |
| Type: | `string` |
| Required: | `false` |
### `input`
Where Kubb reads your spec: a local file path, a URL, inline OpenAPI content as a JSON or YAML string, or an already-parsed object. Kubb detects which one you gave it. Required when an adapter is configured. Omit it in plugin-only mode, when there is no `adapter`.
| | |
| --------: | :----------------------------------- |
| Type: | `string \| Record` |
| Required: | `false` |
A string that starts with `{` or `[`, spans multiple lines, or opens with a YAML `openapi:` or `swagger:` key is read as inline content. Anything else is a file path or a URL, and a relative path resolves against the config file.
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
export default defineConfig({
// a path, a URL, an inline JSON/YAML string, or a parsed object
input: './petStore.yaml',
output: { path: './src/gen' },
})
```
### `output`
Controls where and how files are written.
#### `output.path`
Directory for generated files, absolute or relative to `root`.
| | |
| --------: | :------- |
| Type: | `string` |
| Required: | `true` |
#### `output.mode`
How a plugin consolidates its code into files. Set it on a plugin's `output`, not on the root `output`.
| | |
| --------: | :------------------------------------ |
| Type: | `'file' \| 'directory'` |
| Required: | `false` |
| Default: | follows the shape of `output.path` |
`'file'` writes everything into a single file, so `output.path` must include the extension (`'types.ts'`). `'directory'` writes one file per operation or schema under `output.path`. Pair `'directory'` with `group` to split the output into per-tag or per-path subdirectories.
You rarely need to set this. Leave it out and Kubb reads `output.path`: a path with an extension means one file, a path without one means a directory. Every plugin ships an extensionless default such as `'types'` or `'clients'`, so `pluginTs()` writes a directory without any configuration.
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginAxios } from '@kubb/plugin-axios'
export default defineConfig({
input: './petstore.yaml',
output: { path: './src/gen' },
plugins: [
pluginTs({ output: { path: 'types.ts' } }),
pluginAxios({ output: { path: 'clients', mode: 'directory' }, group: { type: 'tag' } }),
],
})
```
This writes every type into `src/gen/types.ts` and one client file per operation, grouped by tag (`src/gen/clients/pet/`, `src/gen/clients/store/`).
> [!TIP]
> `group` works with the inferred directory mode, no `mode` needed. Set `mode: 'directory'` yourself only to override the inference, such as a directory name that carries a dot (`path: 'clients.v2'`). An explicit `mode: 'file'` still forbids `group` and stops the build with a `KUBB_INVALID_PLUGIN_OPTIONS` error, because a single file has nothing to group.
#### `output.clean`
Wipe `output.path` before regenerating.
| | |
| --------: | :-------- |
| Type: | `boolean` |
| Required: | `false` |
| Default: | `false` |
> [!WARNING]
> Only use `clean: true` with a dedicated output folder. Kubb removes the entire directory.
#### `output.format`
Formatter to run on every generated file.
| | |
| --------: | :---------------------------------------------------- |
| Type: | `'auto' \| 'prettier' \| 'biome' \| 'oxfmt' \| false` |
| Required: | `false` |
| Default: | `false` |
`'auto'` detects the first formatter it finds ([oxfmt](https://oxc.rs) then [Biome](https://biomejs.dev) then [Prettier](https://prettier.io)). A named tool forces that one. `false` skips formatting. Kubb reads your local `.prettierrc` or `biome.json`.
#### `output.lint`
Linter to run after generation.
| | |
| --------: | :--------------------------------------------------- |
| Type: | `'auto' \| 'eslint' \| 'biome' \| 'oxlint' \| false` |
| Required: | `false` |
| Default: | `false` |
`'auto'` detects the first linter it finds ([oxlint](https://oxc.rs) then [Biome](https://biomejs.dev) then [ESLint](https://eslint.org)). A named tool forces that one. `false` skips linting.
#### `output.postGenerate`
Shell commands to run after the generated files are formatted and linted, such as a type check or a custom script. Commands run from the `root` directory, in sequence. Pass a command string, or `{ name, command }` to label a step in the CLI output.
| | |
| --------: | :---------------------------------------------------- |
| Type: | `Array` |
| Required: | `false` |
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
export default defineConfig({
input: './petStore.yaml',
output: {
path: './src/gen',
postGenerate: [{ name: 'types', command: 'npm run typecheck' }, 'biome check --write ./src/gen'],
},
})
```
#### `output.barrel`
Behavior of the root `index.ts` barrel file at `output.path`.
Provided by [`@kubb/plugin-barrel`](/plugins/plugin-barrel/).
| | |
| --------: | :------------------------------------------------------ |
| Type: | `{ type: 'all' \| 'named' } \| false` |
| Required: | `false` |
| Default: | `false` |
`{ type: 'all' }` writes `export * from '...'` for every file. `{ type: 'named' }` writes `export { … } from '...'` using each file's named exports. `false` disables the root barrel.
::code-group
```typescript [named]
// src/gen/index.ts
export { CreatePetRequest, Pet } from './pet'
export { User } from './user'
export type { GetPetQuery } from './operations/getPet'
```
```typescript [all]
// src/gen/index.ts
export * from './pet'
export * from './user'
export * from './operations/getPet'
```
```typescript [false]
// no index.ts generated
```
::
Each plugin keeps its own `output.barrel` for its sub-folder and can override the root setting. Setting `barrel: false` on a plugin disables that plugin's barrel and drops its files from the root barrel. The `nested` flag works at the plugin level only: `{ nested: true }` writes a barrel in every subdirectory so callers can import from any depth. The root `output.barrel` ignores it.
> [!NOTE]
> `pluginBarrel` ships by default and generates nothing until `output.barrel` is set, root or per-plugin.
#### `output.defaultBanner`
Auto-generated banner injected at the top of each file.
| | |
| --------: | :---------------------------- |
| Type: | `'simple' \| 'full' \| false` |
| Required: | `false` |
| Default: | `'simple'` |
`'simple'` adds a short "Generated by Kubb" notice. `'full'` adds the notice plus `Source`, `Title`, and `OpenAPI spec version` from the spec. `false` writes no banner.
::code-group
```typescript [simple]
/**
* Generated by Kubb (https://kubb.dev/).
* Do not edit manually.
*/
```
```typescript [full]
/**
* Generated by Kubb (https://kubb.dev/).
* Do not edit manually.
* Source: petStore.yaml
* Title: Pet Store
* OpenAPI spec version: 1.0.0
*/
```
```typescript [false]
// no banner
```
::
#### `output.banner`
Text prepended to every file a plugin generates. Set it on an individual plugin. The root `output` exposes only [`output.defaultBanner`](#output-defaultbanner). Use it for license headers, lint-disable comments, or framework directives like `'use server'`.
| | |
| --------: | :------------------------------------- |
| Type: | `string \| ((meta: BannerMeta) => string)` |
| Required: | `false` |
A string applies to every file the plugin generates, including barrel (`index.ts`) and group aggregation (`[dir]/[dir].ts`) re-export files. A function runs once per file and receives a `BannerMeta`, so you can vary the banner per file or return an empty string to skip it.
`BannerMeta` extends the document `InputMeta` (`title`, `description`, `version`, …) with per-file context:
| | | |
| -----------: | :-------- | :------------------------------------------------------- |
| `filePath` | `string` | Full output path of the file being generated. |
| `baseName` | `string` | File name only, for example `stocks.ts`. |
| `isBarrel` | `boolean` | `true` for `index.ts` re-export barrels. |
| `isAggregation` | `boolean` | `true` for group `[dir]/[dir].ts` aggregation files. |
> [!NOTE]
> Barrel `index.ts` files stay banner-free by default. They get a banner only when the plugin sets `output.banner`, at which point the function runs with `isBarrel: true`.
#### `output.footer`
Text appended to the end of every file a plugin generates. Mirror of [`output.banner`](#output-banner), with the same `string | ((meta: BannerMeta) => string)` type.
| | |
| --------: | :------------------------------------- |
| Type: | `string \| ((meta: BannerMeta) => string)` |
| Required: | `false` |
### `plugins`
Array of Kubb plugins. Dependencies run first. Missing dependencies fail when a generator requires them with `ctx.requirePlugin`.
| | |
| --------: | :---------------------- |
| Type: | `Array` |
| Required: | `false` |
### `adapter`
Adapter that converts your input into the universal AST. With `defineConfig` from the `kubb` package this defaults to `adapterOas()` from [`@kubb/adapter-oas`](/adapters/adapter-oas/).
See the [Adapter concept](/docs/5.x/explanation/architecture#adapters) for the full picture.
| | |
| --------: | :------------------------------------ |
| Type: | `Adapter` |
| Required: | `false` |
| Default: | `adapterOas()` (included with `kubb`) |
Pass options to customize the adapter:
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { adapterOas } from '@kubb/adapter-oas'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
adapter: adapterOas({ validate: true }),
})
```
### `parsers`
Array of parsers that turn the in-memory file representation into source code. Each parser declares which file extensions it handles through `extNames`.
See the [Parser concept](/docs/5.x/explanation/architecture#parsers) and [`@kubb/parser-ts`](/parsers/parser-ts/) for the built-in parsers.
| | |
| --------: | :--------------------------------------------- |
| Type: | `Array` |
| Required: | `false` |
| Default: | `[parserTs(), parserTsx(), parserMd()]` (included with `kubb`) |
Import parsers explicitly to override the default set:
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { parserTs, parserTsx } from '@kubb/parser-ts'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
parsers: [parserTs(), parserTsx()],
})
```
### `storage`
Storage driver that persists generated files. Defaults to `fsStorage()` (filesystem).
See the [Storage concept](/docs/5.x/explanation/architecture#storage) for the built-in drivers and how to write a custom backend.
| | |
| --------: | :----------------------------------- |
| Type: | `Storage` |
| Required: | `false` |
| Default: | `fsStorage()` |
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { memoryStorage } from 'kubb/kit'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
storage: memoryStorage(),
})
```
### `root`
Project root, absolute or relative to the config file location.
| | |
| --------: | :-------------- |
| Type: | `string` |
| Required: | `false` |
| Default: | `process.cwd()` |
### `reporters`
Reporters available to the run, registered as instances. `defineConfig` registers the built-in `cli`, `json`, `file`, and `html` reporters by default. The HTML reporter remains opt-in: select it with [`--reporter html`](/docs/5.x/reference/commands/generate#reporters). The CLI [`--reporter`](/docs/5.x/reference/commands/generate#reporters) flag selects reporters by name and defaults to `cli`. See that page for details about each reporter.
| | |
| --------: | :------------------------------------------ |
| Type: | `Array` |
| Default: | `[cli, json, file, html]` |
| Required: | `false` |
::studio-cta{source="configuration-reference"}
::
---
# Diagnostics
Source: https://kubb.dev/docs/5.x/reference/diagnostics
---
layout: doc
title: Diagnostics
description: Reference for Kubb's diagnostic codes, the stable codes Kubb prints
when a build fails, with causes and fixes.
outline:
- 2
- 3
order: 6
navigation:
title: Diagnostics
icon: i-iconoir-warning-triangle
---
# Diagnostics
When a build fails, Kubb prints a diagnostic with a stable code, the message, the location in your
document, and a suggested fix. The CLI leads with the code and lists the details below it:
```text [Terminal]
[KUBB_REF_NOT_FOUND]: Could not find a definition for #/components/schemas/Pet.
at: #/components/schemas/Pet
fix: Add the schema under components.schemas, or fix the $ref.
see: https://kubb.dev/docs/5.x/reference/diagnostics#kubb-ref-not-found
```
## Severity
The severity tints the `[CODE]` tag.
| Severity | Color | Effect |
| --- | --- | --- |
| `error` | red | Fails the run with a non-zero exit code. |
| `warning` | yellow | Reported, does not fail the run. |
| `info` | blue | Advisory, does not fail the run. |
## Codes
Find the code printed by Kubb below. Each entry includes its severity and a fix.
## KUBB_ADAPTER_REQUIRED: Adapter required {#kubb-adapter-required}
Severity: `error`.
An action needs an adapter but none is configured.
Set `adapter` in your config, for example `adapterOas()`. Standard `defineConfig` supplies the default OpenAPI adapter, so this catalog code is not currently emitted for an omitted adapter.
## KUBB_BARREL_DUPLICATE_EXPORT: Duplicate barrel export {#kubb-barrel-duplicate-export}
Severity: `error`.
Two files in the same barrel directory export the same name. The barrel keeps the first export and drops the rest, so the generated code still parses, but the run is marked failed.
Rename the colliding declarations, change the resolver, or separate their output directories. A type and a value may share a name.
## KUBB_CLEAN_ROOT: Clean targets the project root {#kubb-clean-root}
Severity: `error`.
`output.clean` removes generated code before a build. Kubb stops the build instead of wiping your project when `output.path` resolves to the project root or a parent of it, which would delete `kubb.config` and every source file.
Use a generated subdirectory such as `./src/gen`. Disable `output.clean` if it contains hand-written files. Kubb rejects cleaning the project root or a parent directory.
## KUBB_DEPRECATED: Deprecated {#kubb-deprecated}
Severity: `info`.
A referenced schema or operation is marked `deprecated`.
Move away from the deprecated definition when possible. Generation continues unchanged, and the notice may recur on each run.
## KUBB_FORMAT_FAILED: Format failed {#kubb-format-failed}
Severity: `error`.
The formatter pass over the generated files failed. Formatting runs after generation. The files are written, but the run is marked failed.
Check that the formatter is installed and its config is valid. Run it against the output directory to inspect the error, or change `output.format`. Files are written, but the run fails.
## KUBB_INPUT_NOT_FOUND: Input not found {#kubb-input-not-found}
Severity: `error`.
The file set as `input` (or passed as `kubb generate PATH`) could not be read. The OpenAPI adapter checks the path before parsing, so the run stops here instead of failing later with a vague read error.
Check that the file exists and is readable. Use a path relative to `kubb.config.ts`, an absolute path, or a full URL for a remote spec.
## KUBB_INPUT_REQUEST_FAILED: Input request failed {#kubb-input-request-failed}
Severity: `error`.
A URL set as `input` answered with a 4xx or 5xx status instead of the OpenAPI document. The server was reached, so either the URL is wrong or the endpoint refused to serve the document.
Fetch the URL with `curl` and confirm it serves the OpenAPI document. For a protected document, download it and use the local file. Check server logs for a 5xx response. This also applies to remote `$ref` requests.
## KUBB_INPUT_REQUIRED: Input required {#kubb-input-required}
Severity: `error`.
An adapter is configured but no `input` was provided, so there is nothing to parse.
Set `input` to a file path, URL, inline JSON/YAML spec, or parsed document. If merging documents, provide at least one input.
## KUBB_INPUT_UNREACHABLE: Input unreachable {#kubb-input-unreachable}
Severity: `error`.
A URL set as `input` never answered, so the request failed before a status came back.
Start the server and check its host and port. Fetch the URL from the machine running Kubb. Connect to any required VPN or proxy, or use a downloaded spec. The message includes the connection failure reason.
## KUBB_INVALID_DOCUMENT: Invalid document {#kubb-invalid-document}
Severity: `error`.
The document resolved from `input` declares neither `openapi` nor `swagger`, so the adapter has nothing to read it as.
Use a document declaring `openapi` or `swagger`. Pass the document itself rather than a wrapper. Missing versions fail even when validation is disabled.
## KUBB_INVALID_SPEC: Invalid spec {#kubb-invalid-spec}
Severity: `warning`.
The document does not pass OpenAPI schema validation. Kubb reports each problem and keeps generating, but the output can be incomplete or wrong. `@kubb/adapter-oas` checks this when [`validate`](/adapters/adapter-oas/reference/options#validate) is `true`, the default.
Fix the reported problem in the spec. Run [`kubb validate`](/docs/5.x/reference/commands/validate) to list every error with its location. Set `validate: false` on the adapter to skip the check.
## KUBB_INVALID_PLUGIN_OPTIONS: Invalid plugin options {#kubb-invalid-plugin-options}
Severity: `error`.
A plugin was given options that cannot be honored together. The main case is `output.mode` resolving to `'file'` while a `group` option is also set, or, for `@kubb/plugin-fetch`/`@kubb/plugin-axios`, while `sdk.mode: 'tag'` is set. Both pair a single-file output with an option that only makes sense split across several files, so the build stops instead of producing a layout the options do not describe.
Single-file output cannot use `group` or `sdk.mode: 'tag'`. Remove grouping, use `sdk.mode: 'flat'`, or write to a directory. A file extension in `output.path` implies file mode unless you explicitly select directory mode.
## KUBB_INVALID_SERVER_VARIABLE: Invalid server variable {#kubb-invalid-server-variable}
Severity: `error`.
A server variable resolves to a value that its `enum` does not allow.
Choose a variable value allowed by its `enum`, correct the default, or update the enum to match the server.
## KUBB_LEGACY_INPUT: Legacy input shape {#kubb-legacy-input}
Severity: `error`.
`input` is a `{ path }` or `{ data }` wrapper. v4 used it to point at a document, and v5 takes the value directly.
Replace `input: { path: './spec.yaml' }` with `input: './spec.yaml'`, or `input: { data: spec }` with `input: spec`.
## KUBB_LINT_FAILED: Lint failed {#kubb-lint-failed}
Severity: `error`.
The linter pass over the generated files failed. Linting runs after generation. The files are written, but the run is marked failed.
Check that the linter is installed and configured. Run it manually against the output. Adjust rules for generated files or change `output.lint`. Files are written, but the run fails.
## KUBB_PATH_TRAVERSAL: Path traversal {#kubb-path-traversal}
Severity: `error`.
A resolved output path escaped the output directory. Kubb refuses to write outside `output.path`, so a crafted spec or a misconfigured `group.name` cannot drop files anywhere on disk.
Keep generated paths within `output.path` and resolver `file.path` overrides within the project root. Reject `..` and path separators in names from the spec or `group.name`.
## KUBB_PERFORMANCE: Performance {#kubb-performance}
Severity: `info`.
Records a plugin's elapsed time. Kubb collects one per plugin during a build.
This bookkeeping code is not printed as a terminal diagnostic. Run `kubb generate --verbose` to inspect plugin timings. The reported duration sums generation time and excludes config loading, formatting, linting, and post-generate hooks.
## KUBB_PLUGIN_FAILED: Plugin failed {#kubb-plugin-failed}
Severity: `error`.
A plugin threw while generating, or reported an error through `ctx.error`. The diagnostic is attributed to the plugin and fails the run.
Check the plugin options and the schema or operation named in the message. Report reproducible plugin bugs with a spec fragment. The diagnostic preserves an underlying error as its cause. For structured errors, plugin authors can use `Diagnostics.report` or `DiagnosticError`.
## KUBB_PLUGIN_INFO: Plugin info {#kubb-plugin-info}
Severity: `info`.
A plugin reported an informational message through `ctx.info`. It is advisory and does not fail the run.
No action is required. The plugin name and message appear in the summary and JSON report. Plugin authors can use `Diagnostics.report` for a stable code and source pointer.
## KUBB_PLUGIN_NOT_FOUND: Plugin not found {#kubb-plugin-not-found}
Severity: `error`.
A plugin requires another plugin that is not in the config.
Install the required plugin and add it to `plugins`, or remove its dependent plugin. Order does not matter. The adapter reads OpenAPI in v5, so do not add `@kubb/plugin-oas`.
## KUBB_PLUGIN_WARNING: Plugin warning {#kubb-plugin-warning}
Severity: `warning`.
A plugin reported a non-fatal warning through `ctx.warn`. Kubb collects and shows it, but it does not fail the run.
Read the message and adjust the input or plugin options as needed. Warnings do not fail the run. Plugin authors can use `Diagnostics.report` for a stable code and source pointer.
## KUBB_POST_GENERATE_FAILED: Post-generate command failed {#kubb-post-generate-failed}
Severity: `error`.
A post-generate command (`output.postGenerate`) exited with a non-zero status. These commands run after generation. The files are written, but the run is marked failed.
Run the command manually from the project root. Check the binary, spelling, and configuration. These commands run after formatting and linting. Files are written, but the run fails.
## KUBB_REF_NOT_FOUND: Reference not found {#kubb-ref-not-found}
Severity: `error`.
A `$ref` points at a definition that does not exist in the document.
Add the missing definition or correct the `$ref`. Run `kubb validate` before generation. Bundle documents with external references when needed.
## KUBB_UNKNOWN: Unknown error {#kubb-unknown}
Severity: `error`.
A fallback for an error that does not yet carry a specific diagnostic code.
Run `kubb generate --reporter file` for a log under `.kubb/`, or use `--verbose`. Inspect the message and stack. Report unclear failures with the message and `Environment:` block. Unclassified errors have no suggested fix or docs URL in CLI output.
## KUBB_UNSUPPORTED_FORMAT: Unsupported format {#kubb-unsupported-format}
Severity: `warning`.
A schema's `format` is not one Kubb maps to a specific type, so it falls back to the base type.
Use a supported format, omit it if the base type is sufficient, or map it through a custom parser or plugin. Generation continues using the base type.
## KUBB_UPDATE_AVAILABLE: Update available {#kubb-update-available}
Severity: `info`.
A newer Kubb version is published on npm than the one running.
Update Kubb and your project plugins through your package manager. The notice never fails a build, and the check is skipped when offline.
## Machine-readable output
`kubb generate --reporter json` prints a stable report to stdout. The output is a JSON array with
one report per config, so a single-config run still prints `[ ... ]`. CI can read diagnostics
without scraping the terminal:
```json [Report]
[
{
"name": "",
"status": "failed",
"plugins": { "passed": 2, "failed": ["plugin-zod"], "total": 3 },
"counts": { "errors": 1, "warnings": 0, "infos": 0 },
"filesCreated": 0,
"durationMs": 312,
"output": "/project/src/gen",
"timings": [{ "plugin": "plugin-ts", "durationMs": 84 }],
"diagnostics": [
{
"code": "KUBB_REF_NOT_FOUND",
"severity": "error",
"message": "Could not find a definition for #/components/schemas/Pet.",
"location": { "kind": "schema", "pointer": "#/components/schemas/Pet" },
"help": "Add the schema under components.schemas, or fix the $ref. Run `kubb validate` to check the spec.",
"docsUrl": "https://kubb.dev/docs/5.x/reference/diagnostics#kubb-ref-not-found"
}
]
}
]
```
Each config emits one report. `counts` totals the `problem` diagnostics by severity. `timings` lists
per-plugin durations slowest first. `name` is the config name, empty when unnamed.
The exit code is unchanged: non-zero on any error. See [`--reporter`](/docs/5.x/reference/commands/generate#reporters)
for the other reporters.
---
# GitHub Actions
Source: https://kubb.dev/docs/5.x/reference/github-actions
---
layout: doc
title: GitHub Actions
description: Inputs, outputs, runtime selection, and snapshot comments for kubb-labs/action.
outline: [2, 3]
order: 7
navigation:
title: GitHub Actions
icon: i-simple-icons-githubactions
---
# GitHub Actions
The `kubb-labs/action@v1` action generates and publishes a Studio snapshot. See [Publish snapshots from CI](/docs/5.x/integrations/ci#github-actions) for a workflow.
## Inputs
| Input | Default | Description |
| ------------------- | --------------------- | ----------------------------------------------------------------------- |
| `token` | | Organization CI API key. Required. |
| `github-token` | ${{ github.token }} | Token that opens the init pull request and writes the snapshot comment. |
| `working-directory` | `.` | Directory holding the Kubb config and the package. |
| `config` | `kubb.config.ts` | Path to the config file, relative to `working-directory`. |
## Outputs
| Output | Description |
| ----------------- | -------------------------------------------- |
| `snapshot-id` | ID Studio stored the snapshot under. |
| `package-name` | Name of the generated package. |
| `package-version` | Version of the generated package. |
| `tarball-url` | URL of the generated tarball. |
| `integrity` | SHA-512 integrity of the tarball. |
| `agent-url` | Studio URL of the CI agent that ran the job. |
| `files-added`, `files-changed`, `files-removed` | Generated files added, changed, and removed since the previous snapshot on the pull request. |
## What a run does
`kubb` runs from the repository's own `node_modules/.bin/kubb` when there is one, so the snapshot matches the version your config and plugins are built against. Otherwise it falls back to `npx`. One CI agent and one comment are reused per pull request, and one per branch.
## What the comment shows
Below the install command, the comment lists which generated files changed:
| Section | Compared with |
| --- | --- |
| Changes against `main` | The latest snapshot of the base branch, from the `push` trigger |
| Changes since `abc1234` | The previous snapshot on the same pull request |
A failed snapshot shows its error in the comment.
---
# JSX renderer
Source: https://kubb.dev/docs/5.x/reference/jsx
---
layout: doc
title: JSX renderer
description: The kubb/jsx surface backed by @kubb/renderer-jsx. jsxRenderer, the
built-in components, and the JSX runtime for component-based code generation.
outline:
- 2
- 3
order: 4
navigation:
title: JSX
icon: i-iconoir-code
---
# JSX renderer
`kubb/jsx` is Kubb's JSX-based renderer, an alternative to building files with the `ast.factory` node builders from [`kubb/kit`](/docs/5.x/reference/kit). It provides a JSX runtime and a set of built-in components so a generator can emit files and markdown through JSX.
## Setup
Point your `tsconfig.json` at the `kubb/jsx` runtime so JSX syntax compiles against its components instead of React's:
```json [tsconfig.json]
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "kubb/jsx"
}
}
```
With that in place, `.tsx` files in the plugin resolve `jsx-runtime` and `jsx-dev-runtime` from `kubb/jsx` automatically.
## `jsxRenderer()`
`jsxRenderer` creates a renderer instance with two members, `render` and `files`. Call `render` with a JSX element, then read the generated files back off `files`.
```tsx twoslash [render.tsx]
import { jsxRenderer } from 'kubb/jsx'
import { File, Function, Type } from 'kubb/jsx'
const renderer = jsxRenderer()
await renderer.render(
{'{ id: number; name: string }'}
{"return fetch('/pets')"}
,
)
const files = renderer.files
```
## Built-in components
`kubb/jsx` groups its built-in components into four sets:
- Core components (`File`, `File.Source`, `File.Import`, `File.Export`) declare an output file and the imports, exports, and source blocks that make it up.
- JavaScript components (`Const`, `Function`, `Function.Arrow`, `Type`) emit TypeScript declarations.
- Markdown components (`Callout`, `Frontmatter`, `Heading`, `List`, `Paragraph`) emit markdown.
- `Jsx` embeds a raw JSX string in the generated output.
| Component | Category | Emits |
| --- | --- | --- |
| [`File`](#file) | Core | File entry |
| [`File.Source`](#file-source) | Core | Source block |
| [`File.Import`](#file-import) | Core | Import statement |
| [`File.Export`](#file-export) | Core | Export statement |
| [`Const`](#const) | JavaScript | TypeScript |
| [`Function`](#function) | JavaScript | TypeScript |
| [`Function.Arrow`](#function-arrow) | JavaScript | TypeScript |
| [`Type`](#type) | JavaScript | TypeScript |
| [`Callout`](#callout) | Markdown | Markdown |
| [`Frontmatter`](#frontmatter) | Markdown | Markdown |
| [`Heading`](#heading) | Markdown | Markdown |
| [`List`](#list) | Markdown | Markdown |
| [`Paragraph`](#paragraph) | Markdown | Markdown |
| [`Jsx`](#jsx-component) | JSX | Raw JSX |
### Core
`File` is the container. Its members `File.Source`, `File.Import`, and `File.Export` attach source blocks, imports, and exports to it.
#### `File` {#file}
Declares a generated output file. Pass both `baseName` and `path` to register a file entry. Omit both to render the children inline without creating a file.
| Prop | Type | Description |
| --- | --- | --- |
| `baseName` | `` `${string}.${string}` `` | File name with extension, such as `petStore.ts`. |
| `path` | `string` | Fully qualified path to the generated file. |
| `meta?` | `object \| null` | Metadata attached to the file node for plugins to read. |
| `banner?` | `string \| null` | Text prepended before the source blocks. |
| `footer?` | `string \| null` | Text appended after the source blocks. |
| `copy?` | `string \| null` | Absolute path to copy verbatim into the output, bypassing the parser. |
| `children?` | `KubbReactNode` | Source blocks, imports, and exports that make up the file. |
```tsx
{`export type Pet = { id: number; name: string }`}
```
#### `File.Source` {#file-source}
Marks a block of source text that belongs to the enclosing `File`. The children are the source string.
| Prop | Type | Description |
| --- | --- | --- |
| `name?` | `string \| null` | Identifies the source for deduplication and barrel generation. |
| `isExportable?` | `boolean \| null` | Prepend the `export` keyword. |
| `isIndexable?` | `boolean \| null` | Include the source in barrel and index generation. |
| `isTypeOnly?` | `boolean \| null` | Mark the source as a type-only export. |
| `children?` | `KubbReactNode` | Source text of the block. |
```tsx
{`export type PetId = string`}
```
#### `File.Import` {#file-import}
Declares an import for the enclosing `File`. The renderer emits it at the top of the generated file.
| Prop | Type | Description |
| --- | --- | --- |
| `name` | `string \| Array` | Binding to import. A string for a default or namespace import, an array for named imports. |
| `path` | `string` | Module specifier to import from. |
| `root?` | `string \| null` | Root used to resolve the import path relative to the file. |
| `isTypeOnly?` | `boolean \| null` | Emit `import type`. |
| `isNameSpace?` | `boolean \| null` | Emit `import * as name`. |
```tsx
// import { useState } from 'react'
// import * as z from 'zod'
```
#### `File.Export` {#file-export}
Declares an export for the enclosing `File`. The renderer emits it at the top of the generated file.
| Prop | Type | Description |
| --- | --- | --- |
| `name?` | `string \| Array \| null` | Binding to export. Omit for a wildcard export. |
| `path` | `string` | Module specifier to export from. |
| `isTypeOnly?` | `boolean \| null` | Emit `export type`. |
| `asAlias?` | `boolean \| null` | Emit `export * as name`. |
```tsx
// export { Pet } from './models/petStore'
// export type * from './models/petStore'
```
### JavaScript
The JavaScript components emit TypeScript declarations. Nest them inside a `File.Source` so the renderer writes them into a `.ts` file.
#### `Const` {#const}
Generates a TypeScript constant declaration. The children are the initializer expression.
| Prop | Type | Description |
| --- | --- | --- |
| `name` | `string` | Identifier of the constant. |
| `export?` | `boolean \| null` | Prepend the `export` keyword. |
| `type?` | `string \| null` | Type annotation written after `const name:`. |
| `asConst?` | `boolean \| null` | Append `as const` after the initializer. |
| `JSDoc?` | `JSDoc \| null` | JSDoc block prepended to the declaration. |
| `children?` | `KubbReactNode` | Initializer expression. |
```tsx
{`z.object({ id: z.number() })`}
// export const petSchema: z.ZodType = z.object({ id: z.number() })
```
#### `Function` {#function}
Generates a TypeScript function declaration. The children are the function body.
| Prop | Type | Description |
| --- | --- | --- |
| `name` | `string` | Identifier of the function. |
| `export?` | `boolean \| null` | Prepend the `export` keyword. |
| `default?` | `boolean \| null` | Emit `default` after `export` to make this the default export. |
| `async?` | `boolean \| null` | Emit `async`. Wraps `returnType` in `Promise<…>`. |
| `params?` | `string \| null` | Parameter list written between the parentheses. |
| `generics?` | `string \| Array \| null` | Generic type parameters. An array joins with commas. |
| `returnType?` | `string \| null` | Return type annotation written after `:`. |
| `JSDoc?` | `JSDoc \| null` | JSDoc block prepended to the declaration. |
| `children?` | `KubbReactNode` | Function body. |
```tsx
{'return client.get("/pets/" + petId)'}
// export async function getPet(petId: string): Promise { … }
```
#### `Function.Arrow` {#function-arrow}
Generates an arrow function assigned to a `const`. Takes every `Function` prop plus `singleLine`.
| Prop | Type | Description |
| --- | --- | --- |
| `singleLine?` | `boolean \| null` | Render a braceless expression body instead of a block. |
```tsx
{`n * 2`}
// export const double = (n: number): number => n * 2
```
#### `Type` {#type}
Generates a TypeScript type alias. The children are the type expression. `name` must start with an uppercase letter or the component throws.
| Prop | Type | Description |
| --- | --- | --- |
| `name` | `string` | Identifier of the type alias, starting with an uppercase letter. |
| `export?` | `boolean \| null` | Prepend the `export` keyword. |
| `JSDoc?` | `JSDoc \| null` | JSDoc block prepended to the declaration. |
| `children?` | `KubbReactNode` | Type expression on the right-hand side. |
```tsx
{`string | number`}
// export type PetId = string | number
```
### Markdown
The markdown components each emit their own source block, so they go directly inside a `File` rather than a `File.Source`. Render the file with `parserMd` so it is written as markdown.
```tsx twoslash [docs.tsx]
import { jsxRenderer } from 'kubb/jsx'
import { File, Frontmatter, Heading, Paragraph, List, Callout } from 'kubb/jsx'
const renderer = jsxRenderer()
await renderer.render(
Pets
{'A pet object with an id and a name.'}
Keep the generator hot with kubb start --watch.
,
)
```
#### `Callout` {#callout}
Renders a GitHub-style alert using the `> [!TYPE]` blockquote syntax.
| Prop | Type | Description |
| --- | --- | --- |
| `type` | `'tip' \| 'note' \| 'important' \| 'warning' \| 'caution'` | Callout kind, mapped to the uppercase label. |
| `title?` | `string \| null` | Title rendered on the marker line. |
| `children` | `string` | Body text. Each line is quoted with `> `. |
```tsx
Breaking change in v6.
// > [!WARNING] Heads up
// > Breaking change in v6.
```
#### `Frontmatter` {#frontmatter}
Emits a YAML frontmatter envelope at the top of a markdown file. Place it as the first child of the `File`.
| Prop | Type | Description |
| --- | --- | --- |
| `data` | `Record` | Object serialized to YAML between `---` fences. |
```tsx
// ---
// title: Pets
// layout: doc
// ---
```
#### `Heading` {#heading}
Renders an ATX-style markdown heading.
| Prop | Type | Description |
| --- | --- | --- |
| `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | Heading depth, matching the number of `#` characters. |
| `children` | `string` | Heading text. |
```tsx
Installation
// ## Installation
```
#### `List` {#list}
Renders a markdown list, one entry per line.
| Prop | Type | Description |
| --- | --- | --- |
| `items` | `ReadonlyArray` | List entries, one per line. |
| `ordered?` | `boolean \| null` | Emit a numbered list. Defaults to a bullet list. |
```tsx
// 1. First
// 2. Second
```
#### `Paragraph` {#paragraph}
Renders a markdown paragraph. Inline markdown passes through verbatim.
| Prop | Type | Description |
| --- | --- | --- |
| `children` | `string` | Paragraph text. |
```tsx
{'A pet object with `id` and `name` fields.'}
```
### JSX
#### `Jsx` {#jsx-component}
Embeds a raw JSX string in the generated source, including fragments. Use it inside a `Function` to emit a component body. The children must be a plain string.
| Prop | Type | Description |
| --- | --- | --- |
| `children?` | `string` | Raw JSX string embedded verbatim. |
```tsx
{'return (\n <>\n Hello\n >\n)'}
```
## See also
- [Kit API](/docs/5.x/reference/kit) for `defineGenerator`'s `renderer` field and the `ast.factory` alternative to JSX
- [Creating plugins](/docs/5.x/tutorials/creating-plugins) for how a generator wires a renderer into its output
- [Kit API: rendering](/docs/5.x/reference/kit/renderers#jsxrenderer-via-kubb-jsx) for how `jsxRenderer` sits next to `createRenderer`
---
# Kit API
Source: https://kubb.dev/docs/5.x/reference/kit
---
layout: doc
title: Kit API
description: The kubb/kit reference for authoring plugins, generators,
resolvers, renderers, adapters, parsers, and storage backends, plus the ast
namespace, Diagnostics, the engine surface, and the testing helpers.
outline:
- 2
- 3
order: 3
navigation:
title: Kit
icon: i-iconoir-tools
---
# Kit API
Import authoring APIs from `kubb/kit`, included with the `kubb` package.
## Big concepts
| Concept | Entry point | What it does |
| --------------------------- | -------------------------------------------- | ------------------------------------------------------------------------ |
| [Plugins](./kit/plugins) | `definePlugin` | The main extension point. Owns file naming, the output folder, and the lifecycle hooks. |
| [Generators](./kit/generators) | `defineGenerator` | Walks the AST and emits files. A plugin registers one or more. |
| [Resolvers](./kit/resolvers) | `createResolver` | Decides file names and output paths. Other plugins read them by name. |
| [Renderers](./kit/renderers) | `createRenderer`, `jsxRenderer` | Turns the elements a generator returns into `FileNode`s. |
| [Adapters](./kit/adapters) | `createAdapter` | Converts an input spec into the universal AST every plugin reads. |
| [Parsers](./kit/parsers) | `defineParser` | Turns a `FileNode` into the source string written to disk. |
| [Storage](./kit/storage) | `createStorage`, `fsStorage`, `memoryStorage`| Decides where generated files land. |
## Other parts
| Part | Entry point | What it does |
| ------------------------------------------ | -------------------------- | --------------------------------------------------------------- |
| [AST and node builders](./kit/ast) | `ast` | The namespace behind `factory` builders, visitors, guards, macros, and printers. |
| [Diagnostics](./kit/diagnostics) | `Diagnostics` | Builds and narrows the structured errors Kubb collects during a build. |
| [Engine and configuration](./kit/engine) | `defineConfig`, `createKubb` | The `kubb`-package surface that runs your plugins. |
| [Lifecycle hooks](./kit/hooks) | `KubbHooks` | Every `kubb:*` hook a build fires, its payload, and when it fires. |
| [Testing](./kit/testing) | `kubb/kit/testing` | Vitest-backed helpers for testing plugins, generators, and adapters. |
## See also
- [Extension model](/docs/5.x/explanation/extensions)
- [Create your first plugin](/docs/5.x/tutorials/creating-plugins)
- [JSX reference](/docs/5.x/reference/jsx)
---
# Adapters
Source: https://kubb.dev/docs/5.x/reference/kit/adapters
---
layout: doc
title: Adapters
description: createAdapter builds an adapter that converts an input
specification into the universal AST. Covers the Adapter interface, the
built-in OpenAPI adapter, and writing your own.
outline:
- 2
- 3
order: 5
navigation:
title: Adapters
icon: i-iconoir-link
---
# Adapters
An adapter converts an input specification into the shared AST.
> [!TIP]
> For OpenAPI 2.0, 3.0, and 3.1 use the official [`@kubb/adapter-oas`](/adapters/adapter-oas/). Kubb picks it for you when you import `defineConfig` from the `kubb` package. Write a custom adapter only when you target a different specification such as AsyncAPI, GraphQL, JSON Schema, or gRPC.
## `createAdapter`
`createAdapter` wraps an adapter factory and types its options.
A minimal adapter declares a name and returns an empty [`InputNode`](/docs/5.x/explanation/architecture#ast). An empty AST emits nothing, so fill `schemas` and `operations` from your spec next.
```typescript twoslash [adapterCustom.ts]
import { ast, createAdapter } from 'kubb/kit'
import type { AdapterFactoryOptions } from 'kubb/kit'
type AdapterCustom = AdapterFactoryOptions<'adapter-custom', { strict?: boolean }, { strict: boolean }>
export const adapterCustom = createAdapter((options) => ({
name: 'adapter-custom',
options: { strict: options?.strict ?? false },
document: null,
async parse(_source) {
return ast.factory.createInput({ schemas: [], operations: [] })
},
async validate() {
// Throw or call ctx.error here when the spec is invalid.
},
}))
```
Wire it into your config with `defineConfig` from `kubb` and pass the adapter:
```typescript [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { adapterCustom } from './adapterCustom.ts'
export default defineConfig({
input: './my-spec.json',
output: { path: './src/gen' },
adapter: adapterCustom({ strict: true }),
plugins: [],
})
```
## Adapter anatomy
Every adapter returned from `createAdapter` matches the `Adapter` interface from [`kubb/kit`](/docs/5.x/reference/kit):
| Property | Type | Required | Purpose |
| ------------ | ----------------------------------------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | `string` | Yes | Unique adapter identifier. Convention is `adapter-`. |
| `options` | `TResolvedOptions` | Yes | Adapter options after defaults are applied. |
| `document` | `TDocument \| null` | Yes | The raw parsed source document, for plugins that need direct access. `null` before `parse()`. |
| `parse` | `(source: AdapterSource) => InputNode \| Promise` | Yes | Convert the spec into the [universal AST](/docs/5.x/explanation/architecture#ast). The build driver consumes the returned `InputNode` directly. |
| `validate` | `(input: string, options?: { throwOnError?: boolean }) => Promise` | Yes | Validate the document at a path or URL without running the full pipeline. |
Cross-references need no adapter hook: every plugin resolves `$ref` imports through [`resolver.imports`](/docs/5.x/reference/kit/resolvers#imports), which defaults each ref to its pointer's last segment. An adapter that renames a schema (for example to break a name collision) stamps `targetName` on every ref node pointing at it, so `resolveRefName` and those imports pick up the emitted name. Refs that keep their segment name need no stamp.
> [!IMPORTANT]
> Throw from `parse()` with a clear, user-facing message when the input is invalid.
## Adapter naming convention
Adapters share the layout of plugins, so [`getResolver`](./generators#defineGenerator), the registry, and the docs find them by inference:
| Surface | Pattern | Example |
| ----------------------------- | -------------------------------------------------- | ------------------- |
| npm package | `@/adapter-` or `kubb-adapter-` | `@kubb/adapter-oas` |
| Adapter runtime name | The spec identifier (lowercase) | `'oas'` |
| Factory export | `adapter` (camelCase) | `adapterOas` |
| Name constant | `adapterName` | `adapterOasName` |
| `AdapterFactoryOptions` alias | `Adapter` (PascalCase) | `AdapterOas` |
Export a `satisfies`-typed name constant so consumers can reuse the runtime name.
Throw from `parse()` or `validate()` with a clear message when the input is invalid. If you rename a schema, set `targetName` on references to preserve imports.
---
# AST and node builders
Source: https://kubb.dev/docs/5.x/reference/kit/ast
---
layout: doc
title: AST and node builders
description: The ast namespace groups the factory node builders, the transform
and collect visitors, the guards, the ref and naming helpers, the macro
engine, and the printer helper behind one import.
outline:
- 2
- 3
order: 8
navigation:
title: AST & node builders
icon: i-iconoir-code-brackets
---
# AST and node builders
## `ast`
`ast` is `kubb/kit`'s namespace for the entire AST surface, the same way TypeScript groups its node constructors under `ts.factory`. It carries the `factory` node builders, the `transform` and `collect` visitors, the guards, the ref and string helpers, and the macro engine.
```typescript twoslash [ast-namespace.ts]
import { ast } from 'kubb/kit'
const root = ast.factory.createInput({
schemas: [ast.factory.createSchema({ name: 'Pet', type: 'object', properties: [] })],
operations: [],
})
```
Node building goes through `ast.factory`. `ast.factory.createFile`, `ast.factory.createSource`, and `ast.factory.createText` build the `FileNode` tree a generator returns.
```typescript twoslash [factory.ts]
import { ast } from 'kubb/kit'
const file = ast.factory.createFile({
baseName: 'pet.ts',
path: './pet.ts',
sources: [ast.factory.createSource({ nodes: [ast.factory.createText('export type Pet = { id: number }')] })],
})
```
For why the AST exists and how it fits the pipeline, see [AST concepts](/docs/5.x/explanation/architecture#ast).
## Schema node types
A `SchemaNode` is discriminated by its `type`. The values fall into three families.
### Structural types
| Type | Description | TypeScript |
| -------------- | --------------------------------------- | ------------------------------- |
| `object` | Object with named properties | `{ name: string; age: number }` |
| `array` | Sequence of items | `string[]` |
| `tuple` | Fixed-length array with typed positions | `[string, number, boolean]` |
| `union` | One of multiple types | `string \| number` |
| `intersection` | Combination of multiple types | `A & B` |
| `enum` | Fixed set of literal values | `'active' \| 'inactive'` |
### Scalar types
| Type | Description | TypeScript |
| --------- | -------------- | ---------- |
| `string` | Text value | `string` |
| `number` | Numeric value | `number` |
| `integer` | Whole number | `number` |
| `bigint` | Large integer | `bigint` |
| `boolean` | True/false | `boolean` |
| `null` | Null value | `null` |
| `any` | Any value | `any` |
| `unknown` | Unknown value | `unknown` |
| `void` | No value | `void` |
| `never` | Never produced | `never` |
### Special types
| Type | Description | Example |
| ---------- | --------------------------- | -------------------------------------- |
| `ref` | Reference to another schema | `Pet` (from `$ref`) |
| `date` | ISO date | `2024-01-15` |
| `datetime` | ISO datetime | `2024-01-15T10:30:00Z` |
| `time` | ISO time | `10:30:00` |
| `uuid` | UUID string | `550e8400-e29b-41d4-a716-446655440000` |
| `email` | Email address | `user@example.com` |
| `url` | URL string | `https://example.com` |
| `blob` | Binary data | Raw bytes |
## Factory functions
Factories return defaulted, fully typed nodes for adapters and generator handlers. Never build AST literals by hand.
```typescript twoslash [factories.ts]
import { ast } from 'kubb/kit'
const root = ast.factory.createInput({
schemas: [ast.factory.createSchema({ name: 'Pet', type: 'object', properties: [] }), ast.factory.createSchema({ name: 'Status', type: 'enum', values: ['active', 'inactive'] })],
operations: [ast.factory.createOperation({ operationId: 'listPets', method: 'GET', path: '/pets' })],
})
```
The `ast.factory` namespace also provides constructors for source files and TypeScript-level artifacts that generators emit:
| Factory | Purpose |
| ------------------------------------------------------------------- | -------------------------------------------------------- |
| `createFile`, `createSource`, `createText` | Build `FileNode`s emitted by generators. |
| `createImport`, `createExport` | Emit `import` / `export` statements. |
| `createConst`, `createFunction`, `createArrowFunction`, `createJsx` | Emit TypeScript declarations and JSX. |
| `createParameter` | Describe operation parameters. |
| `createProperty`, `createType` | Compose object properties and TypeScript types. |
| `createResponse`, `createRequestBody`, `createContent`, `createOutput` | Model responses, request bodies, content entries, and generator outputs. |
| `createBreak` | Emit line breaks between nodes. |
| `update` | Apply an identity-preserving shallow update to any node. |
## Visitors {#visitors}
Two visitor functions cover the common traversal patterns: `transform` rewrites the tree and `collect` gathers nodes. Visitor objects use lowercase, kind-style keys (`input`, `output`, `operation`, `schema`, `property`, `parameter`, `response`). To rewrite nodes inside a plugin, reach for [macros](/docs/5.x/how-to/macros), which add names, ordering, and composition on top of `transform`. For logging, validation, or statistics, `collect` the nodes you care about.
### `transform`: synchronous, returns a new tree
```typescript twoslash [transform.ts]
import { ast } from 'kubb/kit'
const root = ast.factory.createInput({ schemas: [], operations: [] })
const enhanced = ast.transform(root, {
schema(node) {
if (node.type === 'object' && node.additionalProperties === undefined) {
return { ...node, additionalProperties: false }
}
return node
},
operation(node) {
return { ...node, tags: node.tags?.length ? node.tags : ['untagged'] }
},
})
```
Use `transform` to change AST structure, normalize inconsistencies, or annotate nodes.
To apply a change and keep that guarantee, use the `update` factory instead of spreading by hand. It returns the same node when every field you pass already matches:
```typescript twoslash [update.ts]
import { ast } from 'kubb/kit'
const node = ast.factory.createSchema({ name: 'Pet', type: 'object', properties: [] })
ast.factory.update(node, { name: 'Pet' }) // -> same `node` reference (no change)
ast.factory.update(node, { name: 'Animal' }) // -> new node with `name` replaced
```
### `collect`: gather matching nodes
`collect` is a generator function, so it yields matches lazily and you consume it with `for...of` or spread it into an array. When you want the array up front, call `collectSync`, which is the eager counterpart and returns `Array`.
```typescript twoslash [collect.ts]
import { ast } from 'kubb/kit'
const root = ast.factory.createInput({ schemas: [], operations: [] })
const mutations = ast.collectSync(root, {
operation(node) {
return node.method === 'POST' ? node : undefined
},
})
const deprecated = ast.collectSync(root, {
schema(node) {
return 'deprecated' in node && node.deprecated ? node : undefined
},
})
console.log(`POST operations: ${mutations.length}`)
console.log(`Deprecated schemas: ${deprecated.length}`)
```
Use `collect` to stream matches as you find them, and `collectSync` to find specific nodes, filter by a criterion, or build a list for later processing.
## Guards and narrowing {#guards-and-narrowing}
Kubb exports type guards and a `narrowSchema` helper for safe discrimination:
```typescript twoslash [guards.ts]
import { ast } from 'kubb/kit'
const root = ast.factory.createInput({ schemas: [], operations: [] })
for (const node of ast.collect(root, { schema: (node) => node })) {
const obj = ast.narrowSchema(node, 'object')
if (obj) {
console.log(`object with ${obj.properties.length} properties`)
}
if (node.type === 'ref') {
console.log(`reference to: ${node.ref}`)
}
}
for (const node of ast.collect(root, { operation: (node) => node })) {
if (ast.isHttpOperationNode(node)) {
console.log(`${node.method} ${node.path}`)
}
}
```
## Refs and naming helpers
The ref and naming helpers split across two surfaces. `resolveRefName` ships on the `ast` namespace, like the guards and node types. `extractRefName`, `childName`, `enumPropName`, and `syncSchemaRef` are named exports of `kubb/kit` itself, not members of the `ast` namespace (the same split as the built-in macros below).
| Helper | Purpose |
| ---------------- | ------------------------------------------------------------------- |
| `extractRefName` | Turn `'#/components/schemas/Pet'` into `'Pet'`. |
| `resolveRefName` | Resolve the name a ref node emits, preferring its `targetName`. |
| `childName` | Derive a child property name from context. |
| `enumPropName` | Convert an enum value into a valid property name. |
| `syncSchemaRef` | Merge a ref node with its resolved schema, letting usage-site fields (`description`, `nullable`) override. |
```typescript twoslash [refs.ts]
import { extractRefName } from 'kubb/kit'
const name = extractRefName('#/components/schemas/Pet')
// ^?
```
## Schema graph
Analyze how schemas reference each other, to prune unused schemas or wrap circular ones in a lazy construct. `collectUsedSchemaNames` and `findCircularSchemas` ship on the `ast` namespace. `containsCircularRef` is a named export of `kubb/kit` itself, not a member of the `ast` namespace.
| Helper | Purpose |
| ------------------------ | ------------------------------------------------------------------------------------------------- |
| `collectUsedSchemaNames` | Collect the names of every top-level schema transitively used by a set of operations. Pair it with `include` filters to leave unreferenced schemas ungenerated. |
| `findCircularSchemas` | Find every schema that takes part in a circular dependency chain, so those positions can be wrapped in a lazy getter or `z.lazy(() => …)`. |
| `containsCircularRef` | Report whether a schema, or anything nested inside it, references a circular schema. Import it from `kubb/kit` directly, not through `ast`. |
## Constants
| Export | Purpose |
| ------------- | ------------------------------------------ |
| `schemaTypes` | Map of every schema `type` discriminant. |
## Macros
A macro is a named, composable transform built on `transform` that rewrites nodes before printing, adding ordering, gating, and reuse a bare visitor doesn't give you. See [Macros concepts](/docs/5.x/how-to/macros).
| Export | Purpose |
| --------------- | ------------------------------------------------ |
| `defineMacro` | Type a macro and read it as one definition. |
| `composeMacros` | Fold an ordered list of macros into one visitor. |
| `applyMacros` | Run a list of macros over a node tree. |
Kubb also ships built-in macros for common schema normalizations that any adapter can compose with its own. These are named exports of `kubb/kit` itself, not members of the `ast` namespace. See [Built-in macros](/docs/5.x/how-to/macros#built-in-macros) for the full walkthrough.
| Macro | Purpose |
| ------------------------ | ------------------------------------------------------------------------------------------- |
| `macroSimplifyUnion` | Drop union members a broader scalar primitive already covers, such as a multi-value string enum next to `string`. |
| `macroDiscriminatorEnum` | Replace a discriminator property's schema with a string enum of its allowed values. |
| `macroEnumName` | Name an inline enum schema from its parent and property name. |
| `macroRenameSchema` | Rename a schema's declaration and retarget every ref pointing at it in one pass. |
### Macro options and callbacks
A macro carries the per-kind callbacks of a [visitor](/docs/5.x/reference/kit/ast#visitors), plus a `name`, an optional `enforce` order, and an optional `match` predicate.
```typescript [Type definition]
type Macro = {
name: string
enforce?: 'pre' | 'post'
match?: (node: Node) => boolean
schema?(node: SchemaNode, context): SchemaNode | null | undefined
operation?(node: OperationNode, context): OperationNode | null | undefined
// input, output, property, parameter, response
}
```
Each callback returns a replacement node, or `undefined` or `null` to leave the node untouched. A macro that changes nothing returns the original reference, so an unchanged tree is reused, not rebuilt.
## Printers
Lower-level helpers for parsers that turn the AST into source code:
| Export | Purpose |
| --------------- | -------------------------------------- |
| `createPrinter` | Typed helper for creating a `Printer`. |
`createPrinter` takes an `overrides` map to replace the handler for individual schema node types. Inside an override, `this.base(node)` runs the built-in handler the override replaced, so you can wrap its output instead of re-implementing it. Pass overrides through the `overrides` field rather than spreading them into `nodes`, otherwise `this.base` cannot find the original handler. The `printer.nodes` option on `@kubb/plugin-ts`, `@kubb/plugin-zod`, and `@kubb/plugin-faker` feeds this map. See [Override a printer](/docs/5.x/how-to/printers).
Inside a handler, `this.import(ast.factory.createImport(...))` declares an import required by the printed code. After printing, the generator calls `printer.drainImports()` to retrieve the imports and clear the list. See [Import a custom codec](/plugins/plugin-zod/guide/customization#import-a-custom-codec) for a complete example.
See [Parsers concepts](/docs/5.x/explanation/architecture#parsers) for how parsers consume printers.
### Printer handlers and context
The map is keyed by the schema `type` discriminant, such as `'string'`, `'integer'`, `'date'`, `'enum'`, or `'object'`. Supply only the handlers you want to replace and the built-in ones fill in the rest.
```typescript [Type definition]
type PrinterNodes = Partial<{
[K in SchemaType]: (this: Context, node: SchemaNodeByType[K]) => Output | null
}>
```
Handlers run with a `this` context, so write them as regular functions rather than arrow functions. `this.transform(node)` recurses into a nested schema node through the full handler map, overrides included. `this.base(node)` runs the built-in handler your override replaced, so you can wrap its output instead of rebuilding it. `this.options` reads the resolved printer options, such as `arrayType` on `@kubb/plugin-ts` or `direction` on `@kubb/plugin-zod`.
---
# Diagnostics
Source: https://kubb.dev/docs/5.x/reference/kit/diagnostics
---
layout: doc
title: Diagnostics
description: Diagnostics is the namespace a plugin or adapter uses to build and narrow the structured errors Kubb collects during a build, each carrying a stable code, a severity, and a location.
outline: [2, 3]
order: 9
navigation:
title: Diagnostics
icon: i-iconoir-warning-circle
---
# Diagnostics
`Diagnostics` is the namespace a plugin or adapter uses to build and narrow the structured errors Kubb collects during a build. Throw or return a `Diagnostics.Error` instead of a bare `Error` when you want a stable code, a severity, and a location attached.
| Member | Purpose |
| ----------------------- | -------------------------------------------------------------------------|
| `Diagnostics.Error` | Constructs a diagnostic-carrying error with a `code`, `severity`, and `message` |
| `Diagnostics.hasError` | Narrows an array of diagnostics to whether any has `severity: 'error'` |
| `Diagnostics.isProblem` | Guards a diagnostic down to the problem kind (as opposed to `performance` or `update`) |
| `Diagnostics.isPerformance` | Guards a diagnostic down to the `performance` kind, which carries a per-plugin `duration` |
| `Diagnostics.isUpdate` | Guards a diagnostic down to the `update` kind, emitted when a newer Kubb version is available |
See the [Diagnostics reference](/docs/5.x/reference/diagnostics) for the full list of stable codes Kubb ships with, and how `Diagnostics.hasError` and `Diagnostics.isProblem` are used together after a build.
---
# Engine and configuration
Source: https://kubb.dev/docs/5.x/reference/kit/engine
---
layout: doc
title: Engine and configuration
description: The engine that runs your plugins comes from the kubb package and
its kubb/config subpath. Covers defineConfig, createKubb, the Kubb instance,
BuildOutput, and narrowing config.input.
outline:
- 2
- 3
order: 10
navigation:
title: Engine & configuration
icon: i-iconoir-cpu
---
# Engine and configuration
Import configuration helpers from `kubb/config` and the programmatic engine from `kubb`.
## `defineConfig`
`defineConfig` adds TypeScript type-checking to a `kubb.config.ts` file. It fills in defaults for omitted fields.
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
})
```
It accepts a config object, an array of configs, a Promise, or a function. The function form receives the [CLI options](/docs/5.x/reference/commands/) at runtime, so you can toggle behavior on flags like `--watch`:
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
export default defineConfig(({ watch }) => ({
input: './petStore.yaml',
output: { path: './src/gen', clean: !watch },
}))
```
### Defaults applied for omitted fields
| Field | Default |
| ---------------- | ---------------------------------------- |
| `root` | `process.cwd()` |
| `adapter` | [`adapterOas()`](/docs/5.x/explanation/architecture#adapters) |
| `parsers` | `[parserTs(), parserTsx(), parserMd()]` |
| `reporters` | `[cli, json, file, html]` |
| `plugins` | `pluginBarrel()` appended when not already present |
| `output.barrel` | `false` (barrel generation is opt-in, even with `pluginBarrel` in `plugins`) |
| `output.format` | `false` |
| `output.lint` | `false` |
> [!TIP]
> `pluginBarrel` generates nothing until `output.barrel` is set, root or per-plugin. The `kubb` package appends `pluginBarrel` unless already registered.
## `createKubb` {#createkubb}
`createKubb` drives Kubb from your own code. It accepts a `UserConfig` and returns a `Kubb` instance. Calling `.build()` runs the full generation pipeline and returns a `BuildOutput`.
Reach for `createKubb` when you orchestrate several builds, inspect diagnostics, or feed Kubb output into a larger toolchain. For a one-off build, chain the call: `await createKubb(config).build()`.
Import `createKubb` from the `kubb` package. It applies the same defaults as `defineConfig`, so a shared config has the same adapter, parsers, and plugins in both paths. Import it from `@kubb/core` when you need a bare engine without package defaults.
Pass a plain `UserConfig` object.
```typescript twoslash [build.ts]
// @module: esnext
import { createKubb } from 'kubb'
import { Diagnostics } from 'kubb/kit'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginAxios } from '@kubb/plugin-axios'
const kubb = createKubb({
input: './petStore.yaml',
output: { path: './gen' },
plugins: [pluginTs(), pluginAxios()],
})
const { files, storage, driver, diagnostics } = await kubb.safeBuild()
if (Diagnostics.hasError(diagnostics)) {
for (const diagnostic of diagnostics.filter(Diagnostics.isProblem)) {
if (diagnostic.severity === 'error') {
console.error(`${diagnostic.plugin ?? 'kubb'}: ${diagnostic.message}`)
}
}
process.exit(1)
}
// Per-plugin timings are carried as `performance` diagnostics.
for (const { plugin, duration } of diagnostics.filter(Diagnostics.isPerformance)) {
console.log(`${plugin}: ${duration}ms`)
}
console.log(`Generated ${files.length} files`)
const paths = await storage.readKeys()
paths.forEach((path) => console.log(` ${path}`))
```
### `Kubb` instance members (all getters are read-only)
| Member | Type | Description |
| -------------- | ------------------------------ | -------------------------------------------------------------------------------------------------- |
| `.setup()` | `() => Promise` | Initializes the driver and storage. `build()` calls this automatically. |
| `.build()` | `() => Promise` | Runs the full pipeline and throws a `BuildError` when any diagnostic is an error. |
| `.safeBuild()` | `() => Promise` | The canonical call. Runs the full pipeline and collects problems in `BuildOutput.diagnostics` instead of throwing. |
| `.hooks` | `Hookable` | Read-only. Shared hook emitter. Call `.hook(name, handler)` before `build()` to attach a listener. See [lifecycle hooks](./hooks). |
| `.config` | `Config` | Read-only. Resolved config, available right after `createKubb` since it resolves in the constructor. |
| `.storage` | `Storage` | Read-only getter. Final source code keyed by absolute path. Available after `setup()`, throws before. |
| `.driver` | read-only getter | Advanced plugin driver handle, available after `setup()`. Throws if accessed before `setup()`. |
### `BuildOutput` fields
| Field | Type | Description |
| ------------- | ------------------- | -------------------------------------------------------------------------- |
| `files` | `Array` | Generated files with paths, names, and content |
| `storage` | `Storage` | Generated source code accessible via the `Storage` API |
| `driver` | driver handle | Advanced plugin driver handle for introspection |
| `diagnostics` | `Array` | Problems collected during the build, plus a `performance` diagnostic per plugin |
Each `Diagnostic` carries a `code`, a `severity` (`error`, `warning`, or `info`), a `message`, and the `plugin` that produced it. Failed-plugin diagnostics keep the original error on `cause`. A `performance` diagnostic (`kind: 'performance'`) carries a `duration` in milliseconds. Use the `Diagnostics.isProblem`, `Diagnostics.isPerformance`, and `Diagnostics.isUpdate` guards to narrow by kind.
> [!WARNING]
> After `safeBuild()`, check `Diagnostics.hasError(diagnostics)` before processing files. Plugins can fail without `safeBuild()` throwing. `build()` throws a `BuildError` in that case.
## See also
- [Configuration reference](/docs/5.x/reference/configuration)
- [CLI options](/docs/5.x/reference/commands/)
- [`@kubb/plugin-barrel`](/plugins/plugin-barrel/)
- [Programmatic usage recipe](/docs/5.x/how-to/recipes#programmatic-build)
---
# Generators
Source: https://kubb.dev/docs/5.x/reference/kit/generators
---
layout: doc
title: Generators
description: defineGenerator declares a named generator unit that walks the AST
and emits files. Covers the schema, operation, and operations methods and the
GeneratorContext passed to each.
outline:
- 2
- 3
order: 2
navigation:
title: Generators
icon: i-iconoir-code-brackets
---
# Generators
## `defineGenerator` {#defineGenerator}
`defineGenerator` declares a named generator unit consumed by a plugin. Generators walk the [AST](/docs/5.x/explanation/architecture#ast) and emit files. The engine calls each method for the matching node type during the generation loop.
Each generator method returns `TElement | Array | void`. Returning a renderer element (for example JSX from [`kubb/jsx`](/docs/5.x/reference/jsx)) requires a `renderer` factory on the generator.
```typescript twoslash [my-generator.ts]
import { ast, defineGenerator } from 'kubb/kit'
const myGenerator = defineGenerator({
name: 'my-generator',
operation(node, ctx) {
return [
ast.factory.createFile({
baseName: `${node.operationId}.ts`,
path: `./${node.operationId}.ts`,
sources: [
ast.factory.createSource({
nodes: [ast.factory.createText(`export const op = '${node.operationId}'`)],
}),
],
}),
]
},
})
```
### Generator methods
| Method | Input | Output | When to use |
| -------------- | ---------------------------------------- | -------------------------------------- | ------------------------------------------------------------------ |
| `schema()` | `SchemaNode` (per data schema) | `TElement \| Array \| void` | Generate types, validators, factories. Called once per schema |
| `operation()` | `OperationNode` (per API operation) | `TElement \| Array \| void` | Generate hooks, clients, handlers. Called once per operation |
| `operations()` | `Array` (all operations) | `TElement \| Array \| void` | Generate index or barrel files. Called once after all operations |
### Scoping with `match` {#match}
Add a `match(node, ctx)` predicate to skip `schema` or `operation` for nodes a generator does not apply to. A `false` result skips that node. Omitting `match` runs the generator for every node. `match` does not filter `operations()`.
```typescript twoslash [scoped-generator.ts]
import { ast, defineGenerator } from 'kubb/kit'
const getOnlyGenerator = defineGenerator({
name: 'get-only-generator',
match(node, ctx) {
return node.kind === 'Operation' && ast.isHttpOperationNode(node) && node.method.toLowerCase() === 'get'
},
operation(node, ctx) {
// node is already known to be a GET operation here
return
},
})
```
### `GeneratorContext` properties (the `ctx` argument passed to each method)
| Property | Type | Purpose |
| --------------------- | ----------------------------------------------------| ---------------------------------------------------------------------|
| `ctx.config` | `Config` | Resolved Kubb configuration |
| `ctx.root` | `string` | Absolute path to the output directory for the current plugin |
| `ctx.options` | `TResolvedOptions` | Per-node resolved options (after exclude/include/override filtering) |
| `ctx.plugin` | `Plugin` | The owning plugin descriptor |
| `ctx.resolver` | `Resolver` | Resolver for the current plugin |
| `ctx.driver` | `KubbDriver` | Plugin driver for cross-plugin access |
| `ctx.hooks` | `Hookable` | Event bus for `KubbHooks` events |
| `ctx.adapter` | `Adapter` | The adapter that parsed the input spec |
| `ctx.meta` | `InputMeta` | Document metadata from the adapter. Carries `title`, `version`, `baseURL`, and the pre-computed `circularNames` and `enumNames` arrays. |
| `ctx.cache` | `NodeCache` | Cache scoped to the node being generated, shared by every plugin generating from that node in the current pass. Exposes `readItem`, `writeItem`, and `ensureItem` |
| `ctx.addFile()` | `(...files: FileNode[]) => Promise` | Add files, skipping any that already exist |
| `ctx.upsertFile()` | `(...files: FileNode[]) => Promise` | Add or merge files (concatenates sources and imports) |
| `ctx.getPlugin()` | `(name: string) => Plugin \| undefined` | Get a plugin by name |
| `ctx.requirePlugin()` | `(name: string) => Plugin` | Get a plugin by name or throw a descriptive error |
| `ctx.getResolver()` | `(name: string) => Resolver` | Get a resolver by plugin name |
| `ctx.info()` | `(message: string) => void` | Emit an info message via the build event system |
| `ctx.warn()` | `(message: string) => void` | Emit a warning via the build event system |
| `ctx.error()` | `(error: string \| Error) => void` | Emit an error via the build event system |
## See also
- [Generator concepts](/docs/5.x/explanation/extensions#generators)
- [AST concepts](/docs/5.x/explanation/architecture#ast) for node types and traversal
- [Creating plugins](/docs/5.x/tutorials/creating-plugins)
---
# Lifecycle hooks
Source: https://kubb.dev/docs/5.x/reference/kit/hooks
---
layout: doc
title: Lifecycle hooks
description: Every kubb:* hook a build fires, its payload, and when it fires.
Listen with kubb.hooks.hook(name, handler) or from a plugin's hooks map.
outline:
- 2
- 3
order: 11
navigation:
title: Lifecycle hooks
icon: i-iconoir-refresh-double
---
# Lifecycle hooks
Kubb runs on one shared, typed hook emitter, and every phase of a build fires a `kubb:*` hook on it. Plugins listen through their [`hooks`](./plugins) map. Outside code listens through `kubb.hooks`.
Every hook name and its payload lives in the `KubbHooks` type. Handlers can be async, and Kubb awaits each one.
## Listening
Attach a listener with `kubb.hooks.hook(name, handler)` before you call `build()`.
```ts
import { createKubb } from 'kubb'
const kubb = createKubb({
input: './petStore.yaml',
output: { path: './gen' },
})
kubb.hooks.hook('kubb:plugin:end', ({ plugin, duration }) => {
console.log(`${plugin.name} finished in ${duration}ms`)
})
kubb.hooks.hook('kubb:build:end', ({ files }) => {
console.log(`Generated ${files.length} files`)
})
await kubb.build()
```
## Firing order
A full generation run fires the hooks in this order.
::lifecycle-timeline
## Generation run
These wrap a full run. A direct `.build()` or `.safeBuild()` does not fire them.
| Hook | Payload | When it fires |
| ------------------------ | ------------------------------------------------------------- | ------------------------------------------------------ |
| `kubb:lifecycle:start` | `{ version }` | The bundler plugin starts, before anything else |
| `kubb:generation:start` | `{ config }` | A run begins, before setup |
| `kubb:setup:start` | none | Before the driver and storage are initialized |
| `kubb:setup:end` | none | After setup, before the build pipeline |
| `kubb:generation:end` | `{ config, storage, status, diagnostics, filesCreated, hrStart }` | The run finishes, after the output passes |
| `kubb:lifecycle:end` | none | The bundler plugin is done |
## Build pipeline
These come from the pipeline, so `.build()` and `.safeBuild()` fire them too.
| Hook | Payload | When it fires |
| ------------------------ | ------------------------------------------------------------- | ------------------------------------------------------ |
| `kubb:plugin:setup` | `KubbPluginSetupContext` | Once per plugin during setup, to register generators, resolvers, macros, and options |
| `kubb:build:start` | `{ config, adapter, meta, getPlugin, files, upsertFile }` | The AST is ready, before plugins run. Skipped when no adapter parsed input |
| `kubb:plugin:start` | `{ plugin }` | Before a plugin's generators run |
| `kubb:generate:schema` | `(node, ctx)` | For each schema node the adapter produced |
| `kubb:generate:operation`| `(node, ctx)` | For each operation node |
| `kubb:generate:operations`| `(nodes, ctx)` | Once with every operation node |
| `kubb:plugin:end` | `{ plugin, duration, success, error, config, files, upsertFile }` | After a plugin finishes, with its timing and a file snapshot |
| `kubb:plugins:end` | `{ config, files, upsertFile }` | After every plugin has generated, before files hit disk. The spot to add aggregate files like a barrel |
| `kubb:files:processing:start` | `{ files }` | Before the batch of generated files is written |
| `kubb:files:processing:update`| `{ files }` | As files are written. Here `files` holds progress records (`{ processed, total, percentage, source?, file, config }`), not the `FileNode` array the `:start` and `:end` variants carry |
| `kubb:files:processing:end` | `{ files }` | After the batch is written |
| `kubb:build:end` | `{ files, config, outputDir }` | After every file is written |
## Output passes
These run after a successful build, only during a full generation run.
| Hook | Payload | When it fires |
| -------------------- | ------------------------------------ | ------------------------------------------------ |
| `kubb:format:start` | none | Before the formatter runs |
| `kubb:format:end` | none | After formatting |
| `kubb:lint:start` | none | Before the linter runs |
| `kubb:lint:end` | none | After linting |
| `kubb:hooks:start` | none | Before the `postGenerate` commands run |
| `kubb:hook:start` | `{ id, command, name, args }` | Before one `postGenerate` command runs |
| `kubb:hook:line` | `KubbHookLineContext` | For each output line a `postGenerate` command emits |
| `kubb:hook:end` | `KubbHookEndContext` | After one `postGenerate` command finishes |
| `kubb:hooks:end` | none | After every `postGenerate` command |
## Messaging
These carry log messages and diagnostics, and can fire at any point.
| Hook | Payload | When it fires |
| ----------------- | --------------------------- | ---------------------------------------------------- |
| `kubb:info` | `{ message, info }` | An informational message |
| `kubb:success` | `{ message, info }` | A step completed successfully |
| `kubb:warn` | `{ message, info }` | A non-fatal problem |
| `kubb:error` | `{ error, meta }` | An unstructured error, so its stack survives |
| `kubb:diagnostic` | `{ diagnostic }` | A structured [diagnostic](../diagnostics) (warning, info, or an update notice) |
## Related
- [Plugins](./plugins) for the plugin `hooks` map and `KubbPluginSetupContext`
- [Engine and configuration](./engine) for `createKubb`, `.build()`, and `.safeBuild()`
- [Plugin concepts](/docs/5.x/explanation/extensions#plugins) for the lifecycle as prose
- [Diagnostics](../diagnostics) for the shape of `kubb:diagnostic` payloads
---
# Parsers
Source: https://kubb.dev/docs/5.x/reference/kit/parsers
---
layout: doc
title: Parsers
description: defineParser creates a parser that converts a generated file AST
into the source string written to disk. Covers the Parser interface, the
built-in TypeScript parser, and adding your own.
outline:
- 2
- 3
order: 6
navigation:
title: Parsers
icon: i-iconoir-code
---
# Parsers
A parser turns a `FileNode` into source code.
> [!TIP]
> For TypeScript and JavaScript output use the built-in [`@kubb/parser-ts`](/parsers/parser-ts/). It is added by default when you import `defineConfig` from the `kubb` package. Build a custom parser only when you target a different language, such as Python, Kotlin, or Rust.
## `defineParser`
`defineParser` wraps a factory and infers its parser type. The factory receives caller options, or an empty object when omitted. Declare extensions with `extNames`:
```typescript twoslash [parserText.ts]
import { defineParser } from 'kubb/kit'
export const parserText = defineParser(() => ({
name: 'parser-text',
extNames: ['.txt'],
parse(file) {
return file.sources
.flatMap((source) => source.nodes ?? [])
.map((node) => (node.kind === 'Text' ? node.value : ''))
.join('\n')
},
print(...nodes) {
return nodes.map(String).join('\n')
},
}))
```
Wire it into your config:
```typescript [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { parserTs, parserTsx } from '@kubb/parser-ts'
import { parserText } from './parserText.ts'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
parsers: [parserTs(), parserTsx(), parserText()],
})
```
## Parser anatomy
Every value returned from `defineParser` matches the `Parser` interface from [`kubb/kit`](/docs/5.x/reference/kit):
| Property | Type | Required | When called | Purpose |
| ---------- | ------------------------------------------------------------------------- | -------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | `string` | Yes | | Unique parser identifier. Convention is `parser-`. |
| `extNames` | `Array \| undefined` | Yes | | File extensions this parser handles. Set to `undefined` to register a catch-all fallback. |
| `parse` | `(file: FileNode) => string` | Yes | By the file processor after all plugins run | Serializes the file's staged sources into the final output string. Must return synchronously. |
| `print` | `(...nodes: TNode[]) => string` | Yes | By plugins, before files are staged | Renders compiler AST nodes to source text. The node type is parser-specific, for example `ts.Node` for `parserTs`. |
| `copy` | `(file: FileNode, source: string) => UserFileNode` | No | By the file processor, for each `copy` file | Describes a copied template's raw content as nodes, for example its imports as `ImportNode`s, in the same shape `injectFile` takes. Kubb builds it with `createFile` and prints it with `parse`. Omit it to write copied files verbatim. |
> [!IMPORTANT]
> If two parsers register the same extension, the last one in the `parsers` array wins. Order matters.
When no parser matches a file's extension, the file processor joins the file's source strings directly.
## Parser naming convention
Parsers share the layout of [plugins](/docs/5.x/explanation/extensions#plugins) and [adapters](/docs/5.x/explanation/architecture#adapters):
| Surface | Pattern | Example |
| ------------------- | ------------------------------------------------ | -------------------------------- |
| npm package | `@/parser-` or `kubb-parser-` | `@kubb/parser-ts` |
| Parser runtime name | The output language or format (lowercase) | `'typescript'`, `'markdown'` |
| Factory export | `parser` (camelCase) | `parserTs`, `parserMd` |
Call the parser factory when registering it in `parsers`.
> [!TIP]
> Parsers compose by extension. `parserTs` (`.ts`, `.js`) and `parserTsx` (`.tsx`, `.jsx`) ship in the same [`@kubb/parser-ts`](/parsers/parser-ts/) package and register side by side.
Set `extNames: undefined` for a catch-all fallback when no parser matches.
---
# Plugins
Source: https://kubb.dev/docs/5.x/reference/kit/plugins
---
layout: doc
title: Plugins
description: definePlugin wraps a factory into a typed Plugin, with all
lifecycle handlers under one hooks object and the KubbPluginSetupContext that
registers generators, resolvers, macros, and options.
outline:
- 2
- 3
order: 1
navigation:
title: Plugins
icon: i-iconoir-ev-plug
---
# Plugins
Plugins are the main extension point in Kubb. A plugin owns its file naming, its output folder, its lifecycle hooks, and the [generators](./generators) that walk the [AST](/docs/5.x/explanation/architecture#ast) and emit `FileNode` objects.
## `definePlugin`
`definePlugin` wraps a factory function and returns a typed `Plugin`. Lifecycle handlers belong in `hooks`.
```typescript twoslash [plugin-example.ts]
import { definePlugin } from 'kubb/kit'
export const pluginExample = definePlugin((options: { prefix?: string } = {}) => ({
name: 'plugin-example',
hooks: {
'kubb:plugin:setup'(ctx) {
// Register resolvers, generators, and options here.
},
},
}))
```
### Plugin shape
| Property | Type | Required | Description |
| -------------- | ------------------------------------ | -------- | ---------------------------------------------------------- |
| `name` | `string` | Yes | Unique plugin identifier (e.g., `plugin-ts`) |
| `dependencies` | `Array` | No | Names of other plugins this one requires |
| `enforce` | `'pre' \| 'post'` | No | Run this plugin before (`'pre'`) or after (`'post'`) the normal plugins. Dependency order still wins. |
| `options` | `unknown` | No | User-supplied options passed through to generators |
| `hooks` | `{ 'kubb:plugin:setup'?: ...; ... }` | Yes | Lifecycle handlers (see [Plugin API](/docs/5.x/explanation/extensions#plugins)) |
### `KubbPluginSetupContext` methods (passed to `kubb:plugin:setup`)
| Method | Signature | Purpose |
| ---------------- | ----------------------------------------------------------------| ------------------------------------------------------------- |
| `addGenerator` | `(...generators: Array) => void` | Register one or more generators for this plugin |
| `setResolver` | `(resolver: ResolverPatch \| Resolver) => void` | Set or partially override the file naming resolver |
| `addMacro` | `(macro: Macro) => void` | Add a macro that rewrites AST nodes before generators |
| `setMacros` | `(macros: Array) => void` | Replace this plugin's macros with a new list |
| `setOptions` | `(options: ResolvedOptions) => void` | Set the resolved options used by generators |
| `injectFile` | `(file: UserFileNode) => void` | Inject a raw file into the build output, bypassing generation |
| `config` | `Config` | The resolved build configuration at setup time |
| `options` | `TOptions` | The plugin's own options as passed by the user |
> [!IMPORTANT]
> Plugin names should follow the convention `plugin-` (e.g., `plugin-react-query`, `plugin-zod`). See [Creating plugins](/docs/5.x/tutorials/creating-plugins) for naming conventions.
## See also
- [Plugin concepts](/docs/5.x/explanation/extensions#plugins)
- [Creating your first plugin](/docs/5.x/tutorials/creating-plugins)
---
# Renderers
Source: https://kubb.dev/docs/5.x/reference/kit/renderers
---
layout: doc
title: Renderers
description: createRenderer wraps a builder into a Renderer factory for
generators that emit something other than plain FileNodes, alongside
jsxRenderer, the React-free JSX renderer shipped in kubb/jsx.
outline:
- 2
- 3
order: 4
navigation:
title: Renderers
icon: i-iconoir-design-pencil
---
# Renderers
A renderer turns the elements a generator returns into `FileNode`s. Kubb ships a JSX renderer through [`kubb/jsx`](/docs/5.x/reference/jsx). Reach for `createRenderer` only when you want a different templating format.
## `createRenderer`
`createRenderer` takes a builder function and returns a factory that produces a `Renderer`, the object a generator's `renderer` field points at.
`kubb/jsx`'s own `jsxRenderer` ships as a plain factory and does not depend on `createRenderer`, so most plugin authors only reach for `createRenderer` when building an alternative to JSX rendering, for example a renderer that walks a different templating format into `FileNode`s.
## `jsxRenderer` (via `kubb/jsx`) {#jsxrenderer-via-kubb-jsx}
For JSX-based rendering, import `jsxRenderer` from [`kubb/jsx`](/docs/5.x/reference/jsx), backed by the internal `@kubb/renderer-jsx` package.
`jsxRenderer` is a React-free recursive renderer. It walks the JSX components into `FileNode`s without a React reconciliation pass. Components run as plain functions, so hooks and suspense are not available.
```typescript twoslash [renderer.ts]
import { jsxRenderer } from 'kubb/jsx'
const renderer = jsxRenderer()
```
Set the renderer on a generator through its `renderer` field (`renderer: jsxRenderer`) to enable JSX-based output for that generator. Leave it unset, or pass `renderer: null`, to opt out of rendering.
## See also
- [Renderer concepts](/docs/5.x/explanation/extensions#renderers)
- [`jsxRenderer`](#jsxrenderer-via-kubb-jsx), the shipped JSX renderer
- [Creating plugins](/docs/5.x/tutorials/creating-plugins)
---
# Resolvers
Source: https://kubb.dev/docs/5.x/reference/kit/resolvers
---
layout: doc
title: Resolvers
description: createResolver builds a Resolver that controls file naming and path
resolution for a plugin, with auto-injected defaults under resolver.default
and Resolver.merge for layering overrides.
outline:
- 2
- 3
order: 3
navigation:
title: Resolvers
icon: i-iconoir-label
---
# Resolvers
## `createResolver`
`createResolver` builds a plugin resolver for names, paths, and imports.
Provide `pluginName`. Methods use `this` to reach the merged resolver, so namespaced methods call `this.name(...)` for active casing.
```typescript twoslash [resolver.ts]
import { createResolver } from 'kubb/kit'
import type { PluginFactoryOptions, Resolver } from 'kubb/kit'
// Extend the base Resolver with plugin-specific naming namespaces.
type MyResolver = Resolver & {
schema: {
name(node: { name: string }): string
}
}
type MyPlugin = PluginFactoryOptions<'plugin-example', object, object, MyResolver>
export const resolver = createResolver({
pluginName: 'plugin-example',
name(name) {
return `${name.charAt(0).toUpperCase()}${name.slice(1)}`
},
schema: {
name(node) {
return this.name(node.name)
},
},
})
```
### Auto-injected resolver defaults
| Method | Default behavior |
| ---------------- | ---------------------------------------------------------------------- |
| `name` | Active top-level identifier casing. Delegates to `default.name` when omitted |
| `file` | Top-level `FileNode` builder, delegates to `default.file` |
| `default.name` | The core `camelCase` generated-identifier casing |
| `default.options`| Applies `exclude`, `include`, and `override` filters |
| `default.path` | Resolves to `output.path`, with optional tag/path-based subdirectories |
| `default.file` | Constructs a full `FileNode` using the resolver's `file.baseName` casing (default `toFilePath`) |
| `default.banner` | Returns `output.banner` or the standard "Generated by Kubb" header |
| `default.footer` | Returns `output.footer` when set |
### `resolver.imports` {#imports}
`resolver.imports` builds one import entry per `$ref` in a schema tree, so a generator emits the imports for every schema the current node references. Each ref resolves through `ast.resolveRefName`, which prefers the node's `targetName` and falls back to the pointer's last segment.
The resulting names and paths go through the resolver's own `name` and `file` conventions. `extname` defaults to `.ts`.
```typescript twoslash [imports.ts]
import { ast } from 'kubb/kit'
import type { GeneratorContext, PluginFactoryOptions } from 'kubb/kit'
declare const ctx: GeneratorContext
const { resolver, root, output } = { ...ctx, output: { path: 'types' } }
const node = ast.factory.createSchema({ type: 'ref', ref: '#/components/schemas/Pet' })
// ---cut---
const imports = resolver.imports({ node, root, output })
// → [{ kind: 'Import', name: ['pet'], path: '/src/types/pet.ts' }]
```
Pass `name` to override how a referenced schema name becomes the imported identifier, for example to point enum refs at a suffixed type name:
```typescript twoslash [importsName.ts]
import { ast } from 'kubb/kit'
import type { GeneratorContext, PluginFactoryOptions } from 'kubb/kit'
declare const ctx: GeneratorContext
const { resolver, root, output } = { ...ctx, output: { path: 'types' } }
const node = ast.factory.createSchema({ type: 'ref', ref: '#/components/schemas/Pet' })
// ---cut---
const imports = resolver.imports({
node,
root,
output,
name: (schemaName) => `${resolver.name(schemaName)}Type`,
})
```
### `Resolver.merge`
`Resolver.merge(base, patch)` returns a new resolver with `patch`'s fields layered over `base`'s and every helper re-bound. A top-level `name` replaces, while `file` and each namespace merge per member, so overriding `query.name` keeps the base `query.keyName`.
Use it to compose resolvers or apply partial overrides.
> [!TIP]
> Type a patch with `ResolverPatch` to keep `this` and namespace shapes checked against the target resolver.
```typescript twoslash [merge.ts]
import { createResolver, Resolver } from 'kubb/kit'
import type { PluginFactoryOptions } from 'kubb/kit'
const resolver = createResolver({ pluginName: 'plugin-example' })
// ---cut---
const patched = Resolver.merge(resolver, {
name(name) {
return `Custom${this.default.name(name)}`
},
})
```
## Resolver overrides {#resolver-overrides}
`name` changes identifier casing. `file.baseName` and `file.path` control files. Plugin namespaces control specific symbols.
```typescript [Type definition]
type ResolverPatch = {
name?: (name: string) => string
file?: {
baseName?: (params: { name: string; extname: string }) => string
path?: (params: { baseName: string; output: Output }) => string
}
// plugin-specific namespaces, such as query.keyName or schema.typeName
}
```
Methods run with a `this` context bound to the full, merged resolver, so write them as regular functions rather than arrow functions. `this.default.name(name)` always applies Kubb's core `camelCase` default. The plugin preset's `name` method remains separate.
From a namespaced method, `this.name(name)` calls the active top-level `name` method and follows any user override. Calling `this.name` from the top-level `name` method itself recurses, so call an exported preset resolver when you want to wrap its casing.
## See also
- [Resolver concepts](/docs/5.x/explanation/extensions#resolvers)
- [Override a resolver](/docs/5.x/how-to/resolvers)
- [Creating plugins](/docs/5.x/tutorials/creating-plugins)
---
# Storage
Source: https://kubb.dev/docs/5.x/reference/kit/storage
---
layout: doc
title: Storage
description: Storage backends decide where generated files are written. Covers
createStorage, the Storage interface, and the built-in fsStorage and
memoryStorage backends.
outline:
- 2
- 3
order: 7
navigation:
title: Storage
icon: i-iconoir-database
---
# Storage
Storage backends persist generated files in the filesystem, memory, or a custom destination.
## `createStorage`
`createStorage` takes a builder function `(options: TOptions) => Storage` and returns a factory `(options?: TOptions) => Storage`. Call the returned factory to create a backend.
```typescript twoslash [memory-storage.ts]
import { createStorage } from 'kubb/kit'
export const memoryStorage = createStorage(() => {
const store = new Map()
return {
name: 'memory',
async existsItem(key) {
return store.has(key)
},
async readItem(key) {
return store.get(key) ?? null
},
async writeItem(key, value) {
store.set(key, value)
},
async removeItem(key) {
store.delete(key)
},
async readKeys(base) {
const keys = [...store.keys()]
return base ? keys.filter((k) => k.startsWith(base)) : keys
},
async empty(base) {
if (!base) return store.clear()
for (const k of store.keys()) if (k.startsWith(base)) store.delete(k)
},
}
})
```
> [!TIP]
> Use `memoryStorage` for tests and dry runs. Use `fsStorage` for normal development and CI/CD.
## `Storage` interface {#storage-interface}
The engine consumes this interface and exposes the backend through the build result.
| Method | Params | Returns | Purpose |
| -------------- | ---------------------------- | ------------------------- | --------------------------------------------- |
| `existsItem()` | `key: string` | `Promise` | Check whether an item exists |
| `readItem()` | `key: string` | `Promise` | Retrieve an item's content |
| `writeItem()` | `key: string, value: string` | `Promise` | Write an item |
| `removeItem()` | `key: string` | `Promise` | Delete an item |
| `readKeys()` | `base?: string` | `Promise` | List keys, optionally filtered by prefix |
| `empty()` | `base?: string` | `Promise` | Delete all items, optionally scoped by prefix |
Omitting `base` on `empty()` is implementation-defined. `memoryStorage` wipes every entry, while the filesystem-backed `fsStorage` treats a missing `base` as a no-op and deletes nothing.
Every method is required. `readItem` returns `null` for a missing key. Kubb skips `writeItem` when content matches after trimming surrounding whitespace, so writes do not count every generated file.
## `fsStorage`
`fsStorage` is the built-in filesystem storage backend, used by default when no `storage` option is set in the config. It creates output directories automatically and respects `output.path`.
## `memoryStorage`
`memoryStorage` is the built-in in-memory storage backend that writes nothing to disk, so it suits plugin tests, CI validation, and dry runs.
> [!NOTE]
> Both `fsStorage` and `memoryStorage` are exported from `kubb/kit` and can be passed directly to the `storage` field at the root of your config.
## See also
- [Storage concepts](/docs/5.x/explanation/architecture#storage)
- [Configuration reference](/docs/5.x/reference/configuration)
---
# Testing
Source: https://kubb.dev/docs/5.x/reference/kit/testing
---
layout: doc
title: Testing
description: kubb/kit/testing is a separate subpath for the Vitest-backed helpers used to test plugins, generators, and adapters, kept apart from kubb/kit so the authoring toolkit never pulls in Vitest.
outline: [2, 3]
order: 12
navigation:
title: Testing
icon: i-iconoir-flask
---
# Testing
`kubb/kit/testing` is a separate subpath for the Vitest-backed helpers used to test plugins, generators, and adapters. It stays separate from `kubb/kit` so importing the authoring toolkit never pulls Vitest into a plugin's runtime dependencies.
```typescript twoslash [plugin.test.ts]
import { createMockedPlugin, renderGeneratorOperation, matchFiles } from 'kubb/kit/testing'
```
The subpath exports mocking builders, generator runners, and a snapshot matcher:
| Export | What it does |
| --------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `createMockedPlugin` | Builds a minimal plugin for a test without wiring a full config. |
| `createMockedAdapter` | Builds a minimal adapter for a test without wiring a full config. `parse` returns an empty `InputNode` by default. |
| `createMockedPluginDriver` | Builds a driver around a set of plugins so a generator runs through its real lifecycle in isolation. |
| `renderGeneratorSchema` | Calls a generator's `schema()` method against a `SchemaNode` fixture and collects the files it emits. |
| `renderGeneratorOperation` | Calls a generator's `operation()` method against an `OperationNode` fixture and collects the files it emits. |
| `renderGeneratorOperations` | Calls a generator's `operations()` method against an array of `OperationNode` fixtures and collects the files it emits. |
| `matchFiles` | Asserts the generated `FileNode`s match expected paths and contents through file snapshots, the assertion most generator tests end on. |
---
# HTTP serialization
Source: https://kubb.dev/docs/5.x/reference/serialization
---
layout: doc
title: HTTP serialization
description: Default parameter styles and serialized values for generated Fetch and Axios clients.
outline: deep
order: 8
navigation:
title: HTTP serialization
icon: i-iconoir-network
---
# HTTP serialization
Fetch and Axios clients use the OpenAPI operation’s serialization metadata to encode path, query, header, and cookie parameters.
## Parameter styles
The client reads serialization metadata from the generated operation.
### Query
Query parameters default to the `form` style. Arrays explode into repeated keys unless the spec
says otherwise, and `spaceDelimited`, `pipeDelimited`, and `deepObject` change how arrays and
objects collapse.
| Style | `explode` | Input | Result |
| ------------------ | --------- | ------------------ | ----------------- |
| `form` (default) | `true` | `{ id: [3, 4, 5] }` | `id=3&id=4&id=5` |
| `form` | `false` | `{ id: [3, 4, 5] }` | `id=3,4,5` |
| `spaceDelimited` | `false` | `{ id: [3, 4, 5] }` | `id=3%204%205` |
| `pipeDelimited` | `false` | `{ id: [3, 4, 5] }` | `id=3\|4\|5` |
| `deepObject` | `n/a` | `{ a: { b: 1 } }` | `a%5Bb%5D=1` |
With `explode: true`, `spaceDelimited` and `pipeDelimited` fall back to repeated keys like `form`,
so the delimiter only shows with `explode: false`.
### Path
Path parameters default to the `simple` style, which emits the bare value. `label` prefixes a
`.` and `matrix` prefixes a `;name=` segment. The results below are the serialized segment for a
parameter named `id`.
| Style | `explode` | Input | Result |
| ------------------ | --------- | ---------------- | ------------------ |
| `simple` (default) | `false` | `[3, 4, 5]` | `3,4,5` |
| `label` | `true` | `[3, 4, 5]` | `.3.4.5` |
| `matrix` | `true` | `[3, 4, 5]` | `;id=3;id=4;id=5` |
| `simple` | `false` | `{ x: 1, y: 2 }` | `x,1,y,2` |
### Header and cookie
Header parameters use the `simple` style and cookie parameters use the `form` style. Both fix the
style and only let `explode` vary, so the metadata for these locations carries `explode` alone.
Header values are sent as-is, and cookie values are URL-encoded into a single `Cookie` header.
| Location | `explode` | Input | Result |
| -------- | --------- | -------------------------------- | ----------------------- |
| header | `false` | `[3, 4]` | `X-Ids: 3,4` |
| header | `true` | `{ role: 'admin' }` | `X-Filter: role=admin` |
| cookie | `false` | `{ session: 'abc', ids: [1, 2] }` | `session=abc; ids=1,2` |
| cookie | `true` | `{ ids: [1, 2] }` | `ids=1; ids=2` |
For custom serializers, body codecs, response decoding, and validation, see [Configure serialization](/plugins/plugin-fetch/guide/serialization).
---
# Telemetry
Source: https://kubb.dev/docs/5.x/reference/telemetry
---
layout: doc
title: Telemetry
description: Learn what anonymous usage data Kubb collects, how to opt out, and
how the data improves the tool.
outline:
- 2
- 3
order: 5
navigation:
title: Telemetry
icon: i-iconoir-antenna
---
# Telemetry
The Kubb CLI collects anonymous command, plugin, and performance data.
> [!IMPORTANT]
> Telemetry is enabled by default. Disable it any time with the `DO_NOT_TRACK` or `KUBB_DISABLE_TELEMETRY` environment variable.
## What is collected
Telemetry includes these fields:
| Field | Description | Example |
| -------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------- |
| `command` | CLI command that was run | `"generate"`, `"validate"`, `"mcp"` |
| `kubbVersion` | Kubb CLI version | `"5.0.0"` |
| `nodeVersion` | Node.js major version | `"22"` |
| `runtime` | JavaScript runtime name | `"node"`, `"bun"`, `"deno"` |
| `runtimeVersion` | Runtime major version | `"22"` |
| `platform` | Operating system | `"linux"`, `"darwin"`, `"win32"` |
| `ci` | Whether running in CI | `true` |
| `agent` | Name of the AI coding agent that ran the command, if any | `"claude"` |
| `plugins` | Plugin names and their options (only for `generate`) | `[{ "name": "@kubb/plugin-ts", "options": { "output": { "path": "types" } } }]` |
| `duration` | Command execution time in milliseconds | `1432` |
| `filesCreated` | Number of files generated (only for `generate`) | `47` |
| `status` | Whether the command succeeded or failed | `"success"` |
### Commands that send telemetry
| Command | Description |
| ------------------ | ---------------------------------------------------- |
| `kubb generate` | Sent after code generation completes or fails |
| `kubb validate` | Sent after OpenAPI validation completes or fails |
| `kubb mcp` | Sent after the MCP server starts or fails to start |
## What is not collected
Kubb never sends:
- OpenAPI specification contents
- File paths or directory structures
- Secrets, API keys, or tokens
- Source code or generated code
- IP addresses or user identifiers
## How to opt out
Set `DO_NOT_TRACK` or `KUBB_DISABLE_TELEMETRY` to `1` or `true`:
```shell [Terminal]
DO_NOT_TRACK=1 kubb generate
```
Add `export DO_NOT_TRACK=1` to your shell profile to opt out permanently. `DO_NOT_TRACK` follows the [cross-tool convention](https://consoledonottrack.com/).
## Data transmission
Telemetry is formatted as [OpenTelemetry OTLP](https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/) traces. Kubb sends them to `https://otlp.kubb.dev/v1/traces` at the end of each command. The request fires only when you are online. It times out after 5 seconds and fails silently if anything goes wrong.
---
# Ecosystem
Source: https://kubb.dev/docs/5.x/resources/ecosystem
---
layout: doc
title: Ecosystem
description: Discover projects built with Kubb, articles featuring Kubb, and community resources. See how developers use Kubb for OpenAPI code generation.
outline: [2, 3]
---
# Ecosystem
Projects and articles from the community.
## Production projects
- [KOPER](https://github.com/dxloop/koper) is a full-stack framework that uses Kubb for type-safe API integration.
- [Xata MCP Server](https://xata.io/blog/built-xata-mcp-server) is built with Kubb's OpenAPI-to-MCP generation.
- [ClickHouse with MooseStack](https://clickhouse.com/blog/clickhouse-powered-apis-in-react-app-moosestack#bridging-to-the-frontend) powers React-app APIs with Kubb.
- [Fiveonefour](https://docs.fiveonefour.com/moose/apis/openapi-sdk) generates OpenAPI SDKs with Kubb.
## Articles and tutorials
- [API Consumption with OpenAPI and Typed Code](https://www.rocketseat.com.br/blog/artigos/post/consumo-apis-openapi-codigo-tipado) from RocketSeat covers front-end API consumption with Kubb.
- [OpenAPI to Frontend](https://apisyouwonthate.com/newsletter/openapi-to-frontend/) from APIs You Won't Hate uses OpenAPI specs for frontend work.
- [Securing Auth0 API Calls in Next.js](https://medium.com/@laxman.pokhrel.101/from-headache-to-harmony-securing-auth0-api-calls-in-next-js-54fc117bfb92) on Medium combines Kubb, Axios, and the Next.js App Router.
## Listed in
- [TanStack Query Community Projects](https://tanstack.com/query/v5/docs/framework/react/community/community-projects)
- [Zod Ecosystem](https://zod.dev/ecosystem?id=x-to-zod) under "X to Zod" tools
## Community
Ask questions on [Discord](https://discord.gg/shfBFeczrm). Built something with Kubb? Share it on [GitHub Discussions](https://github.com/kubb-labs/kubb/discussions) and we will add it here.
---
# Create, extend, and publish a plugin
Source: https://kubb.dev/docs/5.x/tutorials/creating-plugins
---
layout: doc
title: Create, extend, and publish a plugin
description: Create and test a Kubb plugin, add a configurable comment prefix, and package it for npm.
outline:
- 2
- 3
order: 2
navigation:
title: Create, extend, and publish a plugin
icon: i-iconoir-ev-plug
---
# Create, extend, and publish a plugin
Build a plugin that writes `listPets.ts` containing the method and path of one API operation, add a configurable prefix, then prepare an npm package. You need Node.js 22 or higher, npm, and TypeScript knowledge.
::steps{level="2"}
## Create a project {#_1-create-a-project}
```shell [Terminal]
mkdir kubb-first-plugin
cd kubb-first-plugin
npm init -y
npm pkg set type=module
npm install -D kubb typescript @types/node vitest
mkdir src
```
All authoring APIs come from `kubb/kit`, included with `kubb`.
## Add the specification {#_2-add-the-specification}
Create `petStore.yaml` in the project root:
```yaml [petStore.yaml]
openapi: 3.0.3
info:
title: Pets
version: 1.0.0
paths:
/pets:
get:
operationId: listPets
responses:
'200':
description: A list of pets
```
## Create the plugin {#_3-create-the-plugin}
A plugin factory returns its name and lifecycle hooks. Register a generator and resolver in `kubb:plugin:setup`:
```typescript twoslash [src/plugin.ts]
import { ast, createResolver, defineGenerator, definePlugin } from 'kubb/kit'
const resolver = createResolver({ pluginName: 'plugin-example' })
const operationsGenerator = defineGenerator({
name: 'operation-comments',
operation(node, ctx) {
const name = ctx.resolver.name(node.operationId)
return [
ast.factory.createFile({
baseName: `${name}.ts`,
path: `${ctx.root}/${name}.ts`,
sources: [
ast.factory.createSource({
nodes: [ast.factory.createText(`// ${node.method} ${node.path}\n`)],
}),
],
}),
]
},
})
export const pluginExample = definePlugin(() => ({
name: 'plugin-example',
hooks: {
'kubb:plugin:setup'(ctx) {
ctx.setResolver(resolver)
ctx.addGenerator(operationsGenerator)
},
},
}))
```
The `operation` handler writes a comment containing the operation’s HTTP method and path.
## Generate {#_4-generate}
Register the plugin in your config:
```typescript [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginExample } from './src/plugin'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
plugins: [pluginExample()],
})
```
```shell [Terminal]
npx kubb generate
```
Read the generated file:
```shell [Terminal]
node -e "console.log(require('node:fs').readFileSync('src/gen/listPets.ts', 'utf8'))"
```
It contains a comment naming `GET` and `/pets`.
## Test the output {#_5-test-the-output}
Run an in-process build with memory storage and a small fixture. `createKubb` from `kubb` supplies the same adapter and parser defaults as `defineConfig`.
```typescript [src/plugin.test.ts]
import { describe, expect, it } from 'vitest'
import { createKubb } from 'kubb'
import { memoryStorage } from 'kubb/kit'
import { pluginExample } from './plugin'
const input = {
openapi: '3.0.3',
info: { title: 'Pets', version: '1.0.0' },
paths: {
'/pets': {
get: {
operationId: 'listPets',
responses: { '200': { description: 'A list of pets' } },
},
},
},
}
describe('pluginExample', () => {
it('emits an operation comment', async () => {
const { files, storage } = await createKubb({
input,
output: { path: './gen' },
storage: memoryStorage(),
plugins: [pluginExample()],
}).build()
const file = files.find((file) => file.baseName === 'listPets.ts')
expect(file).toBeDefined()
expect(await storage.readItem(file!.path)).toContain('/pets')
})
})
```
Run the test:
```shell [Terminal]
npx vitest run src/plugin.test.ts
```
Vitest reports one passing test. It checks the generated file in memory without writing to disk.
## Extend the plugin {#extend-the-plugin}
Add a `prefix` option so users can label the generated comment. Replace `src/plugin.ts` with:
```typescript [src/plugin.ts]
import { ast, createResolver, defineGenerator, definePlugin } from 'kubb/kit'
import type { PluginFactoryOptions } from 'kubb/kit'
type ExamplePlugin = PluginFactoryOptions<
'plugin-example',
{ prefix?: string },
{ prefix: string }
>
const resolver = createResolver({ pluginName: 'plugin-example' })
const operationsGenerator = defineGenerator({
name: 'operation-comments',
operation(node, ctx) {
const name = ctx.resolver.name(node.operationId)
return [ast.factory.createFile({
baseName: `${name}.ts`,
path: `${ctx.root}/${name}.ts`,
sources: [ast.factory.createSource({
nodes: [ast.factory.createText(`// ${ctx.options.prefix}${node.method} ${node.path}\n`)],
})],
})]
},
})
export const pluginExample = definePlugin((options = {}) => ({
name: 'plugin-example',
hooks: {
'kubb:plugin:setup'(ctx) {
ctx.setOptions({ prefix: options.prefix ?? '' })
ctx.setResolver(resolver)
ctx.addGenerator(operationsGenerator)
},
},
}))
```
The factory accepts an optional prefix, resolves its default to an empty string, and stores the result with `ctx.setOptions`. The generator reads the resolved value from `ctx.options`.
Change the config to `plugins: [pluginExample({ prefix: 'API: ' })]`, then run `npx kubb generate` again. The generated comment now reads:
```typescript [src/gen/listPets.ts]
// API: GET /pets
```
Add this test inside the existing `describe` block in `src/plugin.test.ts`:
```typescript [src/plugin.test.ts]
it('applies the configured prefix', async () => {
const { files, storage } = await createKubb({
input,
output: { path: './gen' },
storage: memoryStorage(),
plugins: [pluginExample({ prefix: 'API: ' })],
}).build()
const file = files.find((file) => file.baseName === 'listPets.ts')
expect(file).toBeDefined()
expect(await storage.readItem(file!.path)).toContain('// API: GET /pets')
})
```
Run `npx vitest run src/plugin.test.ts` again. Both tests should pass: the original behavior remains available, and the new option changes the output.
For further extensions, use the [Plugin reference](/docs/5.x/reference/kit/plugins) and [Generator reference](/docs/5.x/reference/kit/generators). A dependency on another plugin lets your generator reuse its resolver rather than guessing generated names; see [Dependencies and ordering](/docs/5.x/explanation/extensions#dependencies).
## Build the package {#_7-build-the-package}
Export the factory from a package entrypoint. Use a `.js` extension for the relative import so the compiled ESM works in Node:
```typescript [src/index.ts]
export { pluginExample } from './plugin.js'
```
Create a TypeScript build config:
```json [tsconfig.json]
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"declaration": true,
"skipLibCheck": true,
"rootDir": "src",
"outDir": "dist"
},
"include": ["src/index.ts", "src/plugin.ts"]
}
```
Add the following fields to your existing `package.json`, keeping the development dependencies installed earlier. Replace the package name with an available name you own before publishing:
```json [package.json fields]
{
"name": "kubb-plugin-example",
"version": "1.0.0",
"type": "module",
"files": ["dist"],
"scripts": {
"build": "tsc -p tsconfig.json",
"test": "vitest run src/plugin.test.ts",
"prepack": "npm run build && npm test"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"peerDependencies": { "kubb": "^5.0.0" }
}
```
`kubb` remains a development dependency so you can build and test locally. The peer dependency asks consumers to supply a compatible Kubb version.
Build, inspect the files npm would include, and create a local tarball:
```shell [Terminal]
npm run build
npm pack --dry-run
npm pack
```
The dry run should list `dist/index.js`, `dist/index.d.ts`, `dist/plugin.js`, and `dist/plugin.d.ts`, along with `package.json`. The tarball should contain compiled output rather than the fixture, tests, or generated client files. `prepack` runs the build and both tests before packaging.
## Publish {#_8-publish}
Add a README showing installation and `pluginExample({ prefix: 'API: ' })` in a Kubb config. Choose a license and include its file. Review the tarball and confirm the package name and version before publishing to npm.
When you are ready, sign in to your npm account and publish:
```shell [Terminal]
npm login
npm publish --access public
```
Consumers install your package as a development dependency and import its factory from the package name, rather than from `./src/plugin`.
## See also
- [Extension model](/docs/5.x/explanation/extensions)
- [Plugin API](/docs/5.x/reference/kit/plugins)
- [Lifecycle hooks](/docs/5.x/reference/kit/hooks)
::
---
# Generate your first client
Source: https://kubb.dev/docs/5.x/tutorials/quickstart
---
layout: doc
title: Generate your first client
description: Generate and inspect a typed Axios client from a small OpenAPI specification.
outline:
- 2
- 3
order: 1
navigation:
title: Generate your first client
icon: i-iconoir-rocket
---
# Generate your first client
Generate TypeScript types and an Axios client for one Petstore operation. You need Node.js 22 or higher and npm. You'll inspect the generated operation and its types.
::steps{level="2"}
## Create a project {#_1-create-a-project}
```shell [Terminal]
mkdir kubb-first-client
cd kubb-first-client
npm init -y
npm pkg set type=module
npm install -D kubb typescript @kubb/plugin-ts @kubb/plugin-axios
npm install axios
```
## Add the specification {#_2-add-the-specification}
Create `petStore.yaml` in the project root:
```yaml [petStore.yaml]
openapi: 3.0.3
info:
title: Petstore
version: 1.0.0
servers:
- url: https://petstore.swagger.io/v2
paths:
/pet/{petId}:
get:
operationId: getPetById
parameters:
- name: petId
in: path
required: true
schema:
type: integer
responses:
'200':
description: A pet
content:
application/json:
schema:
$ref: '#/components/schemas/Pet'
components:
schemas:
Pet:
type: object
required: [id, name]
properties:
id:
type: integer
name:
type: string
```
The `getPetById` operation accepts a pet ID and returns a pet with an ID and name.
## Configure generation {#_3-configure-generation}
Create `kubb.config.ts` beside the specification:
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginAxios } from '@kubb/plugin-axios'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen', clean: true },
plugins: [
pluginTs({ output: { path: 'models' } }),
pluginAxios({ output: { path: 'clients' } }),
],
})
```
This configuration writes types to `src/gen/models` and the client to `src/gen/clients`. The generated directory is dedicated to Kubb because `clean: true` removes it before each run.
## Generate the client {#_4-generate-the-client}
```shell [Terminal]
npx kubb generate
```
Kubb reports a successful build. List the generated client files:
```shell [Terminal]
node -e "console.log(require('node:fs').readdirSync('src/gen/clients'))"
```
The output contains `getPetById.ts`. Open that file and notice that `getPetById` accepts typed path parameters and returns a typed result. Its imported types live in `src/gen/models`.
The generated tree includes:
```text [Generated files]
src/gen/
├── .kubb/ # Shared client and serialization helpers
├── clients/
│ └── getPetById.ts
└── models/
├── GetPetById.ts
└── Pet.ts
```
## Regenerate after a change {#_5-regenerate-after-a-change}
Add a `tag` property to the `Pet` schema in `petStore.yaml`, alongside `id` and `name`:
```yaml [petStore.yaml — properties]
tag:
type: string
```
Run `npx kubb generate` again and open `src/gen/models/Pet.ts`. It now includes an optional `tag` field because `tag` is not listed in the schema’s `required` array. The client continues to import the regenerated types.
You now have a typed client and have seen how a specification change flows into its output.
::
## Continue
- [Call generated operations](/plugins/plugin-axios/guide/calling-operations) and [configure their host](/plugins/plugin-axios/guide/base-url).
- [Configure generation](/docs/5.x/how-to/recipes) for React or Vue hooks, validation, and mocks.
- [Interactive setup](/docs/5.x/reference/commands/init) for an existing project.
- [Configuration reference](/docs/5.x/reference/configuration) for supported specification versions, options, and config formats.
---
# Kubb Markdown Parser
Source: https://kubb.dev/parsers/parser-md
---
layout: doc
title: Kubb Markdown Parser
description: Emits `.md` and `.markdown` files from the Kubb AST, joining source
blocks as plain markdown and prepending YAML frontmatter from a file's meta.
outline: deep
kind: parser
id: parser-md
name: Markdown
category: docs
type: official
npmPackage: "@kubb/parser-md"
repo: https://github.com/kubb-labs/kubb
docsPath: /parsers/parser-md
featured: false
maintainers:
- name: Stijn Van Hulle
github: stijnvanhulle
compatibility:
kubb: ">=5.0.0"
node: ">=22"
tags:
- markdown
- frontmatter
- parser
- docs
- yaml
resources:
documentation: https://kubb.dev/parsers/parser-md
repository: https://github.com/kubb-labs/kubb
issues: https://github.com/kubb-labs/kubb/issues
changelog: https://github.com/kubb-labs/kubb/blob/main/packages/parser-md/CHANGELOG.md
---
# @kubb/parser-md
`@kubb/parser-md` emits `.md` and `.markdown` files by joining source blocks with blank lines. It prepends YAML frontmatter from `file.meta.frontmatter`.
The parser takes no options and runs by default alongside `parserTs` and `parserTsx`. A custom `parsers` array replaces that default set. Include every parser your plugins need. Unmatched files are written as source text.
## Installation
::code-group{sync="package-manager"}
```shell [bun]
bun add -d @kubb/parser-md
```
```shell [pnpm]
pnpm add -D @kubb/parser-md
```
```shell [npm]
npm install --save-dev @kubb/parser-md
```
```shell [yarn]
yarn add -D @kubb/parser-md
```
::
## Dependencies
No plugin dependencies. The parser registers on `defineConfig.parsers`.
## Frontmatter
To add a YAML frontmatter block to a generated page, set `frontmatter` on a file's `meta` inside a plugin. The parser renders those keys and prepends them to the output. Any serializable object works.
| | |
| -------: | :-------------------------------- |
| Type: | `Record \| null` |
```typescript [plugin.ts]
import { ast } from 'kubb/kit'
const file = ast.factory.createFile({
baseName: 'README.md',
path: './src/gen/README.md',
meta: {
frontmatter: { title: 'API Reference', layout: 'doc' },
},
sources: [ast.factory.createSource({ nodes: [ast.factory.createText('# API Reference')] })],
})
```
The parser turns that meta into:
```markdown
---
title: API Reference
layout: doc
---
```
You can also call `parserMd().print` directly to build a frontmatter envelope. It accepts objects and markdown strings and joins them with blank lines, so `parserMd().print({ title: 'Pets', layout: 'doc' })` returns `---\ntitle: Pets\nlayout: doc\n---`.
## Example
Register the Markdown parser explicitly when overriding the default parser set.
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb'
import { parserMd } from '@kubb/parser-md'
import { parserTs, parserTsx } from '@kubb/parser-ts'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
parsers: [parserTs(), parserTsx(), parserMd()],
plugins: [],
})
```
## See also
- [Changelog](https://github.com/kubb-labs/kubb/blob/main/packages/parser-md/CHANGELOG.md)
---
# Kubb TypeScript Parser
Source: https://kubb.dev/parsers/parser-ts
---
layout: doc
title: Kubb TypeScript Parser
description: Prints the Kubb AST to TypeScript source with the official
TypeScript compiler, so every plugin writes real `.ts`, `.tsx`, `.js`, and
`.jsx` files.
outline: deep
kind: parser
id: parser-ts
name: TypeScript
category: typescript
type: official
npmPackage: "@kubb/parser-ts"
repo: https://github.com/kubb-labs/kubb
docsPath: /parsers/parser-ts
featured: true
icon:
light: https://kubb.dev/feature/typescript.svg
maintainers:
- name: Stijn Van Hulle
github: stijnvanhulle
compatibility:
kubb: ">=5.0.0"
node: ">=22"
tags:
- typescript
- tsx
- parser
- printer
- ast
resources:
documentation: https://kubb.dev/parsers/parser-ts
repository: https://github.com/kubb-labs/kubb
issues: https://github.com/kubb-labs/kubb/issues
changelog: https://github.com/kubb-labs/kubb/blob/main/packages/parser-ts/CHANGELOG.md
---
# @kubb/parser-ts
`@kubb/parser-ts` prints Kubb's AST as TypeScript using the official [TypeScript compiler](https://www.typescriptlang.org/). It resolves imports, emits exports and JSDoc, and applies the [`extension`](./reference/options#extension) mapping.
- `parserTs()` handles `.ts` and `.js`.
- `parserTsx()` handles `.tsx` and `.jsx`.
Both run by default alongside `parserMd`. A custom `parsers` array replaces that default set. Include every parser your plugins need. Unmatched files are written as source text.
## Installation
::code-group{sync="package-manager"}
```shell [bun]
bun add -d @kubb/parser-ts
```
```shell [pnpm]
pnpm add -D @kubb/parser-ts
```
```shell [npm]
npm install --save-dev @kubb/parser-ts
```
```shell [yarn]
yarn add -D @kubb/parser-ts
```
::
## Dependencies
No plugin dependencies. The parser registers on `defineConfig.parsers`.
## Example
Override import extensions while keeping every default parser registered.
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb'
import { parserTs, parserTsx } from '@kubb/parser-ts'
import { parserMd } from '@kubb/parser-md'
import { pluginTs } from '@kubb/plugin-ts'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
parsers: [
parserTs({ extension: { '.ts': '.js' } }),
parserTsx(),
parserMd(),
],
plugins: [pluginTs()],
})
```
## See also
- [Changelog](https://github.com/kubb-labs/kubb/blob/main/packages/parser-ts/CHANGELOG.md)
---
# Options
Source: https://kubb.dev/parsers/parser-ts/reference/options
---
layout: doc
title: Options
description: Configuration options for @kubb/parser-ts. Rewrite the import and export extensions the TypeScript parser emits.
outline: deep
---
# Options
`parserTs` and `parserTsx` accept the same single option.
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| [`extension`](#extension) | `Record` | `{ '.ts': '' }` | Rewrite the extensions emitted in `import`/`export` statements |
### extension
Rewrites the extensions emitted in `import`/`export` statements. Keys are the source extension, values the output, and `''` drops it. Only the module-specifier string changes, never the on-disk filename.
Use it to emit `.js` imports from `.ts` sources for an ESM dual package, or to keep the source extension for Node16/NodeNext resolution.
The same mapping applies to runtime templates a plugin copies into the output, such as the `.kubb/` runtime files of `@kubb/plugin-fetch`. Their top-level `import` and `export … from` statements become import and export nodes, so the whole output follows one convention.
| Type | Default |
| --- | --- |
| `Record` | `{ '.ts': '' }` |
```typescript
parserTs({ extension: { '.ts': '.js' } }) // import './api.js' instead of './api'
parserTs({ extension: { '.ts': '.ts' } }) // import './api.ts' instead of './api'
```
---
# Kubb Axios Plugin
Source: https://kubb.dev/plugins/plugin-axios
---
layout: doc
title: Kubb Axios Plugin
description: Generates a type-safe axios client from your OpenAPI spec, one
async function per operation, so each call stays in sync with the API.
outline: deep
guides:
- id: authentication
title: Authenticate
- id: base-url
title: Set base URL
- id: calling-operations
title: Call operations
- id: error-handling
title: Handle errors
- id: interceptors
title: Add interceptors
- id: serialization
title: Configure serialization
- id: transport
title: Use custom transport
kind: plugin
id: plugin-axios
name: Axios
category: client
type: official
npmPackage: "@kubb/plugin-axios"
repo: https://github.com/kubb-labs/plugins
docsPath: /plugins/plugin-axios
featured: true
icon:
light: https://kubb.dev/feature/axios.svg
maintainers:
- name: Stijn Van Hulle
github: stijnvanhulle
compatibility:
kubb: ">=5.0.0"
node: ">=22"
tags:
- api-client
- axios
- http-client
- codegen
- openapi
- validator
dependencies:
- plugin-ts
resources:
documentation: https://kubb.dev/plugins/plugin-axios
repository: https://github.com/kubb-labs/plugins
issues: https://github.com/kubb-labs/plugins/issues
changelog: https://github.com/kubb-labs/plugins/blob/main/packages/plugin-axios/CHANGELOG.md
codesandbox: https://codesandbox.io/p/github/kubb-labs/plugins/main/examples/axios
---
# @kubb/plugin-axios
`@kubb/plugin-axios` generates a typed async function for each OpenAPI operation using [Axios](https://axios-http.com/). Calls accept grouped request parameters and return a status-keyed result.
## Installation
::code-group{sync="package-manager"}
```shell [bun]
bun add -d @kubb/plugin-axios
```
```shell [pnpm]
pnpm add -D @kubb/plugin-axios
```
```shell [npm]
npm install --save-dev @kubb/plugin-axios
```
```shell [yarn]
yarn add -D @kubb/plugin-axios
```
::
## Dependencies
Add [`pluginTs`](/plugins/plugin-ts/) or [`pluginZod`](/plugins/plugin-zod/) with `inferred: true` for operation types. `pluginTs` takes precedence when both are configured. Validation also requires `pluginZod`. Install Axios v1 or higher in the consuming app.
## Example
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginAxios } from '@kubb/plugin-axios'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
plugins: [
pluginTs(),
pluginAxios({ baseURL: 'https://petstore.swagger.io/v2' }),
],
})
```
## Documentation
- [Options](./reference/options)
- [Authenticate](./guide/authentication)
- [Set base URL](./guide/base-url)
- [Call operations](./guide/calling-operations)
- [Handle errors](./guide/error-handling)
- [Add interceptors](./guide/interceptors)
- [Serialize parameters](./guide/serialization)
- [Use custom transport](./guide/transport)
## See also
- [axios](https://axios-http.com/)
- [`@kubb/plugin-ts`](/plugins/plugin-ts/)
- [Changelog](https://github.com/kubb-labs/plugins/blob/main/packages/plugin-axios/CHANGELOG.md)
---
# Authenticate your API client
Source: https://kubb.dev/plugins/plugin-axios/guide/authentication
---
layout: doc
title: Authenticate your API client
description: Add authentication to the client Kubb generates. Resolve bearer, basic, and apiKey credentials from your OpenAPI security schemes with one auth callback.
outline: deep
---
# Authenticate your API client
Set an `auth` resolver on the generated Fetch or Axios client. Operations use the security schemes declared in your OpenAPI spec. Requests remain unauthenticated until you provide credentials.
## Set the auth resolver
Set the resolver for all secured operations:
```typescript
import { client } from './gen/.kubb/client'
client.setConfig({
auth: () => localStorage.getItem('token') ?? undefined,
})
```
The resolver is a token string, or a function (which can be async) that returns one or `undefined` to skip a scheme. When an operation accepts more than one scheme, the runtime tries each in turn until one hands back a token.
## Return the right token per scheme
Use the scheme passed to the resolver to select a credential:
```typescript
type Auth = {
type: 'http' | 'apiKey' | 'oauth2' | 'openIdConnect'
scheme?: 'bearer' | 'basic'
name?: string
in?: 'header' | 'query' | 'cookie'
}
```
The scheme decides where the runtime puts the token:
| Scheme in the spec | `Auth` object | Where the token goes |
| ------------------ | ---------------------------------------- | ------------------------------- |
| `http` bearer | `{ type: 'http', scheme: 'bearer' }` | `Authorization: Bearer ` |
| `http` basic | `{ type: 'http', scheme: 'basic' }` | `Authorization: Basic ` |
| `apiKey` header | `{ type: 'apiKey', name, in: 'header' }` | request header named `name` |
| `apiKey` query | `{ type: 'apiKey', name, in: 'query' }` | query parameter named `name` |
| `apiKey` cookie | `{ type: 'apiKey', name, in: 'cookie' }` | `Cookie` header |
| `oauth2` | `{ type: 'oauth2' }` | `Authorization: Bearer ` |
| `openIdConnect` | `{ type: 'openIdConnect' }` | `Authorization: Bearer ` |
> [!NOTE]
> For basic auth, return the raw `username:password` string. The runtime base64-encodes it and writes the `Basic` prefix, so you never build the header yourself.
When a spec mixes schemes, branch on the `Auth` object and return the matching credential:
```typescript
client.setConfig({
auth: (auth) => (auth.type === 'apiKey' ? apiKey : accessToken),
})
```
## Use a separate client per environment
To give each environment its own token, such as one per tenant in a multi-tenant app, build an isolated client with `createClient`:
```typescript
import { createClient } from './gen/.kubb/client'
const tenant = createClient({
baseURL: 'https://tenant.example.com',
auth: () => tenantToken,
})
const { data } = await getPetById({ path: { petId: 1 }, client: tenant })
```
To override the client for one request, pass `auth` on that single call, which suits a one-off token refresh. An explicit `headers` value you set on a call always wins over the resolved token.
For authentication outside OpenAPI security schemes, use an async [request interceptor](/plugins/plugin-fetch/guide/interceptors) to sign requests or supply custom headers.
## See also
- [Interceptors](/plugins/plugin-fetch/guide/interceptors)
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
- [OpenAPI security scheme object](https://spec.openapis.org/oas/v3.1.0#security-scheme-object)
---
# Set your own baseURL
Source: https://kubb.dev/plugins/plugin-axios/guide/base-url
---
layout: doc
title: Set your own baseURL
description: Set up base URLs for API clients generated by Kubb. Environment-specific configuration and dynamic base URLs.
outline: deep
---
# Set the API base URL
Set `baseURL` on the client plugin or generated client. OpenAPI server URLs do not configure the client automatically. The adapter’s server option only sets document metadata.
## Use the baseURL option
Pass `baseURL` to the client plugin to set the generated default. Include `pluginTs`, which the client requires.
::code-group
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginAxios } from '@kubb/plugin-axios'
export default defineConfig({
input: './petStore.yaml',
output: {
path: './src/gen',
},
plugins: [
pluginTs(),
pluginAxios({ baseURL: 'https://localhost:8080/api/v1' }),
],
})
```
::
A value containing a `${...}` interpolation stays dynamic. The plugin emits it as a template literal in the generated client config, so `baseURL: '${process.env.API_URL}'` reads the environment variable when the app runs instead of baking in the build-time value.
## Set it at runtime
Use `client.setConfig` to configure all operations at startup:
```typescript
import { client } from './gen/.kubb/client'
client.setConfig({ baseURL: import.meta.env.VITE_API_URL })
```
Use `createClient` for an isolated client, then pass it to an operation:
```typescript
import { createClient } from './gen/.kubb/client'
import { getPetById } from './gen/clients/getPetById'
const staging = createClient({ baseURL: 'https://staging.petstore.swagger.io/v2' })
const { data } = await getPetById({ path: { petId: 1 }, client: staging })
```
Pass `baseURL` on a single call to override the client for that one request:
```typescript
import { getPetById } from './gen/clients/getPetById'
const { data } = await getPetById({ path: { petId: 1 }, baseURL: 'https://localhost:3000' })
```
A `baseURL` set on the call wins over `createClient`, which wins over `setConfig`, which wins over the build-time value.
## See also
- [Custom transport](/plugins/plugin-fetch/guide/transport)
- [Authentication](/plugins/plugin-fetch/guide/authentication)
- [Interceptors](/plugins/plugin-fetch/guide/interceptors)
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
---
# Call operations
Source: https://kubb.dev/plugins/plugin-axios/guide/calling-operations
---
layout: doc
title: Call operations
description: Call the functions Kubb generates from your OpenAPI spec. Pass typed path, query, header, cookie, and body parameters, then read the status, data, and native response off one result.
outline: deep
---
# 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.
```typescript
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`:
```typescript
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](/plugins/plugin-fetch/guide/serialization).
## Read the result
A resolved call returns a `RequestResult` discriminated by the numeric `status`:
```typescript
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:
```typescript
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:
```typescript
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](/plugins/plugin-fetch/guide/error-handling).
## Unwrap the success body
Call `.unwrap()` to return the success body. Awaiting the operation directly returns the full result.
```typescript
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:
```typescript
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`:
```typescript
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](/plugins/plugin-fetch/guide/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:
```typescript
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:
```typescript
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](/plugins/plugin-fetch/guide/serialization).
## Reuse one configuration
Every generated function imports a shared `client`. Call `setConfig` once at startup and every
call picks up the change:
```typescript
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:
```typescript
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](/plugins/plugin-fetch/reference/options#validator) for request validation and per-direction settings.
```typescript [kubb.config.ts]
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:
```typescript
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:
```typescript [src/app.ts]
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](/plugins/plugin-fetch/guide/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:
```typescript
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
```
## See also
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
- [Error handling](/plugins/plugin-fetch/guide/error-handling)
- [Serialization](/plugins/plugin-fetch/guide/serialization)
- [Server-sent events](/plugins/plugin-fetch/guide/server-sent-events)
- [Set your own baseURL](/plugins/plugin-fetch/guide/base-url)
- [Authentication](/plugins/plugin-fetch/guide/authentication)
- [Custom transport](/plugins/plugin-fetch/guide/transport)
---
# Error handling
Source: https://kubb.dev/plugins/plugin-axios/guide/error-handling
---
layout: doc
title: Error handling
description: Handle failures from the client Kubb generates. Catch a ResponseError, switch to a returned error result with throwOnError, and tell a network failure apart from a 4xx or 5xx response.
outline: deep
---
# Error handling
Generated Fetch and Axios clients throw on non-2xx responses by default. Set the generated default on the client plugin. Individual operations can override it:
```typescript
import { getPetById } from './gen/clients/getPetById'
import { ResponseError } from './gen/.kubb/client'
try {
const { data } = await getPetById({ path: { petId: 1 } })
console.info(data.name)
} catch (error) {
if (ResponseError.is(error)) {
console.error(error.status) // 404
console.error(error.data) // the parsed error body
}
}
```
A `ResponseError` includes the HTTP method and URL (without query parameters, keeping credentials safe) in its `message`, e.g. `GET https://api.example.com/pets/1 failed with status 404 Not Found`.
The error exposes these fields:
```typescript
class ResponseError extends Error {
static is(error: unknown): error is ResponseError
data: TError // the parsed error body
status: number
statusText: string
contentType: string | undefined
request: Request // AxiosRequestConfig on plugin-axios
response: Response // AxiosResponse on plugin-axios
}
```
Use `ResponseError.is(error)` to narrow errors across generated packages, which each bundle their own class. It checks the error name.
## Return the error instead
Pass `throwOnError: false` and the call resolves for every documented status. The result is a
discriminated union: `data` is set on success and `error` on failure, so branch on `status` or
check which field is present.
```typescript
const result = await getPetById({ path: { petId: 1 }, throwOnError: false })
if (result.error) {
console.error(result.status, result.error)
} else {
console.info(result.data.name)
}
```
Check `status` to narrow responses to a specific documented status code.
## Set the generated default
Set `throwOnErrorDefault` on `@kubb/plugin-fetch` or `@kubb/plugin-axios` to choose how generated
operations handle non-2xx responses by default. Each call can still override that setting with
`throwOnError`:
```typescript
import { pluginFetch } from '@kubb/plugin-fetch'
pluginFetch({ throwOnErrorDefault: false })
// resolves with an error result
const list = await searchPets({ query: { status: 'available' } })
// opt one call back into throwing
const pet = await getPetById({ path: { petId: 1 }, throwOnError: true })
```
The generated operation passes its selected value to the client runtime, so setting
`throwOnError` with `client.setConfig` does not change this default. Configure the plugin to
change the generated default.
## Network failures still throw
`throwOnError` governs the response status, not the send: a dropped connection, DNS failure, or
aborted `AbortSignal` rejects regardless of the setting. A `ResponseError` means the server
answered with a non-2xx, and anything else means the request never completed.
```typescript
try {
const { data } = await getPetById({ path: { petId: 1 }, throwOnError: false })
use(data)
} catch (error) {
// not a ResponseError: the request never completed
console.error('request failed to send', error)
}
```
Pass an `AbortSignal` to cancel a request. The call rejects with the abort reason.
## Validation failures
When you turn on the [`validator`](/plugins/plugin-fetch/guide/serialization) option, a body
that does not match its schema throws a `ParseError` instead of returning. It carries the raw
`issues` from the schema, so the same handling works across Zod, valibot, and arktype:
```typescript
import { ParseError } from './gen/.kubb/standardSchema'
try {
const { data } = await getPetById({ path: { petId: 1 } })
use(data)
} catch (error) {
if (error instanceof ParseError) {
console.error(error.issues) // [{ message, path }]
}
}
```
A `ParseError` reports schema validation issues. A `ResponseError` reports a non-2xx status. On the non-throwing path, configured error schemas validate the error body separately from success schemas.
Calling `.unwrap()` on a `throwOnError: false` call turns that same `error` into a rejection, so a
`try`/`catch` works there too. See
[unwrap the success body](/plugins/plugin-fetch/guide/calling-operations#unwrap-the-success-body).
## See also
- [Call operations](/plugins/plugin-fetch/guide/calling-operations)
- [Serialization](/plugins/plugin-fetch/guide/serialization)
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
- [Custom transport](/plugins/plugin-fetch/guide/transport)
---
# Interceptors
Source: https://kubb.dev/plugins/plugin-axios/guide/interceptors
---
layout: doc
title: Interceptors
description: Run code on every call the Kubb client makes. Add or rewrite headers before the send, observe or transform the response, react to errors, and register, replace, or remove handlers at runtime.
outline: deep
---
# Interceptors
Register handlers on `client.interceptors.request`, `.response`, or `.error` to apply behavior across calls. Fetch handlers receive resolved request/result objects. Axios handlers receive native Axios objects.
## Run before the send
Request handlers run after URL construction, serialization, and authentication. Return the modified request:
::code-group
```typescript [Fetch]
import { client } from './gen/.kubb/client'
client.interceptors.request.use((request) => {
request.headers['X-Request-ID'] = crypto.randomUUID()
return request
})
```
```typescript [Axios]
import { client } from './gen/.kubb/client'
client.interceptors.request.use((request) => {
request.headers.set('X-Request-ID', crypto.randomUUID())
return request
})
```
::
On fetch the handler receives a `ResolvedRequest` (`url`, `method`, `headers`, `body`, `signal`,
`credentials`, `options`, `responseType`). On axios it receives an `InternalAxiosRequestConfig`,
so headers go through `AxiosHeaders` and the body is on `data`.
## Run after the send
Response handlers run before calls resolve. Fetch handlers run before the status split and deserialization, so `data` is the raw body.
::code-group
```typescript [Fetch]
client.interceptors.response.use((result) => {
console.info(`${result.status} ${result.request.url}`)
return result
})
```
```typescript [Axios]
client.interceptors.response.use((response) => {
console.info(`${response.status} ${response.config.url}`)
return response
})
```
::
The fetch handler receives a `TransportResult` (`data`, `status`, `statusText`, `headers`,
`contentType`, `request`, `response`), the axios handler an `AxiosResponse`.
## React to an error
Use an error handler to react to HTTP errors on the throwing path:
::code-group
```typescript [Fetch]
client.interceptors.error.use((error) => {
if (error.status === 401) scheduleTokenRefresh()
return error
})
```
```typescript [Axios]
client.interceptors.error.use((error) => {
if (error.response?.status === 401) scheduleTokenRefresh()
return error
})
```
::
The fetch handler receives the `ResponseError`, the axios handler an `AxiosError`, and this
channel only fires on the throw path. When you read with `throwOnError: false`, a documented
non-2xx response resolves and no error interceptor runs, so inspect the returned `error` on the
result instead. A transport failure (no response at all) still throws on both, but only axios
fires the error channel for it, since the channel is wired directly into its own rejection
handling, while fetch has no result to hand the interceptor stacks. See
[error handling](/plugins/plugin-fetch/guide/error-handling) for that path.
## Add, replace, and remove handlers
`use` registers a handler and returns an id. Pass that id to `eject` to remove it, or to `update`
to swap its function in place. Response and error handlers run in the order they were registered,
and so do request handlers on `plugin-fetch`, but `plugin-axios` delegates to axios, which runs
them in reverse registration order.
```typescript
const id = client.interceptors.request.use((request) => request)
client.interceptors.request.update(id, (request) => {
request.headers['X-Trace'] = 'on'
return request
})
client.interceptors.request.eject(id)
```
## Where to set them
Register handlers on the shared `client` for all operations, or on a separate `createClient` instance for isolated calls. Interceptors are instance-level settings.
On `@kubb/plugin-axios`, interceptors registered through `client.interceptors` also follow a custom
transport set later with `client.setConfig({ transport })`. When the transport changes, handlers
detach from the old instance and attach to the new one, and their IDs stay valid for `eject` and
`update`. Clearing the transport (`client.setConfig({ transport: undefined })`) moves them back to the
client's base instance. A per-call `transport` still bypasses client interceptors.
## See also
- [Call operations](/plugins/plugin-fetch/guide/calling-operations)
- [Error handling](/plugins/plugin-fetch/guide/error-handling)
- [Authentication](/plugins/plugin-fetch/guide/authentication)
- [Custom transport](/plugins/plugin-fetch/guide/transport)
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
---
# Configure serialization
Source: https://kubb.dev/plugins/plugin-axios/guide/serialization
---
layout: doc
title: Configure serialization
description: Override parameter serializers, add body codecs, and configure response decoding and validation.
outline: deep
---
# Configure serialization
Fetch and Axios clients encode parameters using OpenAPI `style` and `explode`, serialize request bodies by content type, and decode responses by media type. Generated operations carry the metadata. Most requests need no additional configuration.
## Override parameter serialization
Check the [default parameter styles and encoding](/docs/5.x/reference/serialization#parameter-styles) before replacing a serializer.
Override `serializer.query`, `.body`, or `.path` on the client. Omitted functions retain their defaults:
```typescript
import { client } from './gen/.kubb/client'
import qs from 'qs'
client.setConfig({
serializer: {
query: (params) => qs.stringify(params, { arrayFormat: 'brackets' }),
},
})
```
A serializer set this way runs for every call, but you can pass `serializer` on a single call to
override just that request.
## Encode request bodies
The request content type decides how the body is encoded. The default serializer handles the
common types: a plain object becomes JSON, `multipart/form-data` becomes `FormData`, and
`application/x-www-form-urlencoded` becomes `URLSearchParams`. Binary and already-encoded bodies
(`FormData`, `URLSearchParams`, `Blob`, `ArrayBuffer`, typed arrays, and strings) pass through
untouched.
When an operation declares a single request content type, Kubb sets it on the generated function,
so you pass only the body. For an operation that accepts more than one, set
[`contentType`](/plugins/plugin-fetch/guide/calling-operations#set-the-content-type) on the
call.
> [!NOTE]
> When the body is `FormData`, the runtime removes any `Content-Type` header so the transport sets
> it with the multipart boundary. You do not need to set `multipart/form-data` yourself, and a
> value you set is dropped for that request.
To encode a content type the default serializer does not handle, register a codec for that media
type. `codecs` is keyed by content type, and each entry holds a `serialize` for the request body
and a `deserialize` for the response, so either half is optional:
```typescript
import { client } from './gen/.kubb/client'
import { stringify } from 'yaml'
client.setConfig({
codecs: {
'application/yaml': { serialize: (body) => stringify(body) },
},
})
```
## Decode responses
The runtime reads the response `Content-Type` and decodes the body by it: JSON is parsed, text
stays a string, and a binary type becomes a `Blob`. The negotiated media type is on the result as
`contentType`, so a `switch (result.contentType)` narrows `data` for an operation that returns
more than one.
To decode a media type the runtime does not handle, register a codec's `deserialize` for it. It
receives the raw body and the content type and returns the parsed value, and runs before
validation, so a custom format is transformed first and then checked against its schema:
```typescript
import { client } from './gen/.kubb/client'
client.setConfig({
codecs: {
'application/xml': { deserialize: (raw) => new DOMParser().parseFromString(raw as string, 'application/xml') },
},
})
```
Like the other config, `codecs` can also be passed on a single call, and a per-call entry merges
over the client one for that content type.
When the runtime picks the wrong parse mode because a response omits its `Content-Type` or sets a
misleading one, force the mode with `responseType` on the call. It accepts `'json'`, `'text'`,
`'blob'`, `'arraybuffer'`, and `'document'` (`@kubb/plugin-axios` adds `'formdata'`):
```typescript
const { data } = await downloadInvoice({ path: { id: '123' }, responseType: 'blob' })
// data is a Blob even when the server leaves Content-Type unset
```
For `responseType: 'stream'`, see [server-sent events](/plugins/plugin-fetch/guide/server-sent-events).
For bidirectional formats, supply both `serialize` and `deserialize` in the media type codec, then set the operation’s request and response `contentType`.
## Validate responses
Enable `validator` on the client plugin and register `pluginZod` in the same configuration. Validation is disabled by default.
```typescript twoslash
import { pluginFetch } from '@kubb/plugin-fetch'
pluginFetch({ validator: 'zod' })
```
`'zod'` validates the success response body, and the error body when a non-2xx call does not
throw. Use the object form to opt in per direction, where `request` validates the request
body before the call goes out:
```typescript
pluginFetch({ validator: { request: 'zod', response: 'zod' } })
```
With a validator set, Kubb passes the matching schema to each generated call, and the runtime
parses the body through it. The schemas are Standard Schema compatible, so this works the same
with Zod, valibot, and arktype. A body that does not match throws a `ParseError` carrying the
schema's `issues`, covered in
[error handling](/plugins/plugin-fetch/guide/error-handling#validation-failures).
## See also
- [HTTP serialization reference](/docs/5.x/reference/serialization)
- [Call operations](/plugins/plugin-fetch/guide/calling-operations)
- [Error handling](/plugins/plugin-fetch/guide/error-handling)
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
- [`@kubb/plugin-zod`](/plugins/plugin-zod/)
---
# Use a custom transport
Source: https://kubb.dev/plugins/plugin-axios/guide/transport
---
layout: doc
title: Use a custom transport
description: Replace the transport the generated client sends through. Wrap fetch with retries or pass a pre-configured axios instance, while Kubb keeps owning URLs, serialization, and auth.
outline: deep
---
# Use a custom transport
Set a transport at runtime to replace the network send. The client still builds URLs, serializes parameters, resolves authentication, and handles results.
Fetch accepts a transport function. Axios accepts an `AxiosInstance`. Use [interceptors](/plugins/plugin-fetch/guide/interceptors) for request headers or response logging.
## Fetch: a transport function
`@kubb/plugin-fetch` types the transport as a function that receives a fully resolved request and returns a result:
```typescript
type Transport = (request: ResolvedRequest) => Promise
type ResolvedRequest = {
url: string
method: string
headers: Record
body?: RequestBody
signal?: AbortSignal
credentials?: RequestCredentials
options?: FetchOptions
responseType?: ResponseType
}
type TransportResult = {
data: TData
status: number
statusText: string
headers: Headers
contentType?: string
request: Request
response: Response
}
```
The core hands you a `ResolvedRequest` with the URL built, the query and body serialized, and auth headers set. Return the parsed `data` along with the native `request` and `response`, so status, headers, and the raw body stay reachable on the result.
### Mock the network in tests
Return a fixed result from an isolated client in tests:
```typescript
import { createClient } from './gen/.kubb/client'
import { getPetById } from './gen/clients/getPetById'
const testClient = createClient({
transport: async (request) => ({
data: { id: 1, name: 'Fluffy' },
status: 200,
statusText: 'OK',
headers: new Headers(),
request: new Request(request.url),
response: new Response(),
}),
})
const { data } = await getPetById({ path: { petId: 1 }, client: testClient })
// ^ { id: 1, name: 'Fluffy' }
```
Pass the isolated client per operation or to a query plugin.
## Axios: a custom instance
`@kubb/plugin-axios` types the transport as an `AxiosInstance`. The default is `axios.create()`, and you replace it with your own pre-configured instance:
```typescript
type ClientConfig = {
// ...
transport?: AxiosInstance
}
```
Kubb forwards the resolved URL, query, body, and authentication to the instance.
### Pass a pre-configured instance
Give the client an instance with a timeout, default headers, and a logging interceptor:
```typescript
import axios from 'axios'
import { client } from './gen/.kubb/client'
const instance = axios.create({
timeout: 10_000,
headers: { 'X-Client': 'kubb' },
})
instance.interceptors.response.use((response) => {
console.info(`${response.config.method?.toUpperCase()} ${response.config.url} -> ${response.status}`)
return response
})
client.setConfig({ transport: instance })
```
Every generated function now sends through your instance, so its timeout, headers, and interceptors apply to each call. Any client interceptors registered through `client.interceptors` also transfer to the new transport automatically, preserving their IDs for `eject` and `update`.
> [!NOTE]
> Kubb sets `transformRequest`, `paramsSerializer`, and `validateStatus` on each request so its own serialization and `throwOnError` handling stay in charge. Configure cross-cutting concerns like timeouts, retries, and interceptors on the instance instead of overriding those fields. For a native axios field on a single call, such as `timeout` or `onUploadProgress`, pass [`options`](/plugins/plugin-axios/guide/calling-operations#pass-native-client-options) instead of building a new instance.
## Where to set it
Use `client.setConfig({ transport })` for shared configuration, `createClient({ transport })` for an isolated instance, or pass `transport` on an operation to override it for one call.
## See also
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
- [Interceptors](/plugins/plugin-fetch/guide/interceptors)
- [Authentication guide](/plugins/plugin-fetch/guide/authentication)
- [Set your own baseURL](/plugins/plugin-fetch/guide/base-url)
---
# Options
Source: https://kubb.dev/plugins/plugin-axios/reference/options
---
layout: doc
title: Options
description: Configuration options for @kubb/plugin-axios.
outline: deep
---
# Options
Options for `@kubb/plugin-axios`, which generates a type-safe HTTP client pinned to axios.
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| [`output`](#output) | `Output` | `{ path: 'clients', barrel: { type: 'named' } }` | Where the generated files are written and exported |
| [`group`](#group) | `Group` | — | Split output into per-tag or per-path folders |
| [`baseURL`](#baseurl) | `string` | — | Base URL prepended to every request |
| [`throwOnErrorDefault`](#throwonerrordefault) | `boolean` | `true` | Default error behavior and return type for generated operations |
| [`validator`](#validator) | `false \| 'zod' \| { request?: 'zod'; response?: 'zod' }` | `false` | Validate request and response bodies with Zod |
| [`comments`](#comments) | `'full' \| 'brief' \| 'none'` | `'full'` | How much of each description reaches the JSDoc |
| [`sdk`](#sdk) | `{ mode?: 'tag' \| 'flat'; name?: string }` | — | Emit a class-based SDK instead of standalone functions |
| [`returnType`](#returntype) | `'full' \| 'data'` | `'full'` | Shape of the value a generated call resolves to |
| [`include`](#include) | `Array` | — | Keep only operations that match |
| [`exclude`](#exclude) | `Array` | `[]` | Skip operations that match |
| [`override`](#override) | `Array` | `[]` | Apply different options per pattern |
| [`resolver`](#resolver) | `ResolverPatch` | — | Customize generated names and file paths |
| [`macros`](#macros) | `Array` | — | Rewrite AST nodes before printing |
### output
Where the generated `.ts` files are written and how they are exported.
#### output.path
Folder where the plugin writes its files, defaulting to `'clients'` and resolved against the global `output.path` on `defineConfig`. For a single file, set `output.mode: 'file'` and give `path` a name with its extension, such as `'clients.ts'`.
#### output.mode
`'file'` writes everything into a single file whose `output.path` must include the extension. `'directory'` writes one file per operation under `output.path`. Leave it unset and Kubb reads `output.path`: a name with an extension means one file, anything else a directory.
> [!IMPORTANT]
> `group` requires directory output. Kubb infers the mode from `output.path`. Set `mode: 'directory'` to override that inference. Combining `group` with `mode: 'file'` stops generation with `KUBB_INVALID_PLUGIN_OPTIONS`.
#### output.barrel
Toggle the export style and depth to see the generated barrels.
::barrel-tree
::
Controls how the generated `index.ts` (barrel) re-exports the output. Accepts `{ type: 'named' }` or `{ type: 'all' }`, optionally with `nested: true` (for example `{ type: 'named', nested: true }`) to write an `index.ts` in every subdirectory, or `false` to skip the barrel entirely. Kubb reads the plugin's own `output.barrel` first, falls back to `config.output.barrel` on `defineConfig`, and finally to `false`. Every generator plugin ships a default `output` that sets `barrel: { type: 'named' }`, but passing your own `output` replaces that object wholesale, so repeat `barrel` whenever you set `output` yourself.
#### output.banner
Text added to the top of every generated file, such as a license header or `@ts-nocheck` directive. Pass a string, or a function `(meta: BannerMeta) => string` that receives the document info (`title`, `description`, `version`, `baseURL`) and per-file context (`filePath`, `baseName`, `isBarrel`, `isAggregation`), so a directive can skip barrel files.
#### output.footer
Text added to the bottom of every generated file (`string` or `(meta: BannerMeta) => string`), like `banner` but for closing comments. Pair `banner: '/* eslint-disable */'` with `footer: '/* eslint-enable */'` to scope a lint disable to the generated file.
### group
Switch the mode to see where these operations land on disk.
::grouping-diagram
::
Splits generated files into subfolders by the operation's tag or URL path, each under `{output.path}/{groupName}/`. Without `group`, every file lands directly in `output.path`. It applies only to `output.mode: 'directory'`.
> [!IMPORTANT]
> Combining `group` with `output.mode: 'file'` stops the build with a `KUBB_INVALID_PLUGIN_OPTIONS` error.
#### group.type
Property used to assign each operation to a group (`'tag' | 'path'`), required whenever `group` is set. An operation with no tag goes in the `default` group.
- `'tag'` uses the operation's first tag.
- `'path'` uses the first URL segment, such as `pet` for `/pet/{petId}`.
#### group.name
Function `(context: { group: string }) => string` that turns a group key into a folder name and wins over the default, which camelCases the tag or uses the first path segment.
### baseURL
Base URL prepended to every request. When omitted, no host is prepended and each request uses the operation's relative path from the spec, with no server-URL fallback. A value containing a `${...}` interpolation is emitted as a template literal, so `baseURL: '${process.env.API_URL}'` reads the environment variable at runtime.
### throwOnErrorDefault
Set `throwOnErrorDefault: false` to return documented error responses as values by default. This sets the fallback on each generated request and the default `ThrowOnError` type parameter on standalone functions and SDK methods. A call with `throwOnError: true` still throws for a non-2xx response and narrows its return type to successful responses.
```typescript
pluginAxios({ throwOnErrorDefault: false })
const result = await getPetById({ path: { petId: 1 } })
if (result.error) console.error(result.error)
```
This setting applies to the whole plugin and cannot be set in `override`. Generated operations use it even when the client config changes; pass `throwOnError` on a call to override it. Query hooks continue to set `throwOnError: true` explicitly.
### validator
Validates request and response bodies with schemas from `@kubb/plugin-zod`, which you add to the plugins list when either direction is `'zod'`. `false` (the default) skips validation and returns the response cast to the generated type. `'zod'` validates the success response body, plus the error body when a non-2xx call does not throw. `{ request?: 'zod', response?: 'zod' }` opts in per direction, and with validation on the generated function throws a `ParseError` on invalid data.
Add the schema plugin alongside the client. The following configuration validates both directions:
```typescript [kubb.config.ts]
import { defineConfig } from 'kubb'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginZod } from '@kubb/plugin-zod'
import { pluginAxios } from '@kubb/plugin-axios'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
plugins: [
pluginTs(),
pluginZod(),
pluginAxios({ validator: { request: 'zod', response: 'zod' } }),
],
})
```
### comments
Controls generated JSDoc. `'full'` (default) keeps complete descriptions. `'brief'` keeps the first sentence and other tags. Descriptions over 150 characters without a sentence ending are cut at the last word before 120. `'none'` omits JSDoc but keeps the generated-by banner.
### sdk
Generates a class-based SDK instead of standalone functions, where each tag client is an instance class whose constructor takes a client config and builds its own client, so every environment is a separate instance. `mode: 'tag'` (the default) emits one class per tag such as `PetClient` and `StoreClient`. Add `sdk.name` to also emit a composed root that instantiates every tag client from one shared config, reached as `new PetStore(config).pet.getPetById(...)`. `mode: 'flat'` emits a single class named by `sdk.name` with every operation as a direct method. Leave `sdk` unset to keep the per-operation functions the query plugins consume.
`mode: 'tag'` needs one file per tag, so pairing it with a single-file `output` (`output.mode: 'file'`, or an `output.path` that already names a file such as `'clients.ts'`) throws [`KUBB_INVALID_PLUGIN_OPTIONS`](/docs/5.x/reference/diagnostics#kubb-invalid-plugin-options). Use `mode: 'flat'` for a single-file SDK, or give `output.path` a directory so `mode: 'tag'` can split per tag.
Construct a class with a `ClientConfig` (`baseURL`, `headers`, and so on), then call a method with the grouped options object (`{ path, query, headers, body }`) and read `data` off the result.
```typescript
import { PetClient } from './src/gen/clients/petClient'
const pet = new PetClient({ baseURL: 'https://petstore.swagger.io/v2' })
const { data } = await pet.getPetById({ path: { petId: 1 } })
```
Each call resolves to `{ status, data, error, contentType, request, response }`. With the default `throwOnErrorDefault: true` setting, a resolved call means the request succeeded and `data` is set. Pass `throwOnError: false` to get the discriminated union instead, keyed on the top-level `status`.
```typescript
const { status, data, error } = await pet.getPetById({ path: { petId: 1 }, throwOnError: false })
if (status === 200) {
console.log(data) // data is the success body, error is undefined
} else {
console.error(status, error) // status is the documented error code, error is its parsed body
}
```
### returnType
Shape of the value a generated call resolves to. `'full'` (the default) keeps `{ status, data, error, contentType, request, response }`. `'data'` unwraps that down to the bare success body when `throwOnError` is `true`, and falls back to the full result when `throwOnError` is `false`, since that result still needs `error` to tell success from failure.
```typescript
pluginAxios({ returnType: 'data' })
```
```typescript
const pet = await getPetById({ path: { petId: 1 } }) // Pet, not { status, data, ... }
```
This applies to the standalone functions and the class-based SDK. `@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, and `@kubb/plugin-mcp` read the same option, so their hooks and tool handlers give you the success body as `data` either way.
To read response headers such as `ETag` under `'data'`, pass `throwOnError: false` on the call. It then resolves to the full result, and a non-2xx comes back on `error` instead of throwing:
```typescript
const result = await getPetById({ path: { petId: 1 }, throwOnError: false })
if (result.error === undefined) {
const etag = result.response.headers.etag
}
```
Dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, and `@kubb/plugin-mcp`) also honor per-operation `returnType` set through [`override`](#override), so their generated hooks and handlers match the shape of the resolved ``.
### include
Generates only the operations and schemas that match at least one entry, and skips the rest. Each entry filters by `tag`, `operationId`, `path`, `method`, `contentType`, or `schemaName`, with a `pattern` that can be a string or a `RegExp`, both matched as a regular expression against the value. A string pattern is compiled with `new RegExp(pattern)`, so it is not an exact match: `pattern: 'pet'` also matches `'petType'` or `'superpet'`.
```typescript [Type definition]
export type Include = {
type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
pattern: string | RegExp
}
```
### exclude
Skips any operation or schema that matches at least one entry, the opposite of `include`. Entries use the same `type` and `pattern` fields as `include`, and when both options match an item, `exclude` wins.
When operations are excluded on a client plugin (`@kubb/plugin-fetch` or `@kubb/plugin-axios`), dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, `@kubb/plugin-mcp`) skip generating hooks or handlers for those operations automatically, without requiring duplicate `exclude` configurations.
### override
Applies different plugin options to operations that match a pattern. Each entry takes the same `type` and `pattern` as `include`, plus an `options` object that accepts any plugin option except `override`, so rules cannot nest. The first matching entry merges onto the plugin defaults, and later entries do not stack.
```typescript [Type definition]
export type Override = {
type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
pattern: string | RegExp
options: Omit, 'override'>
}
```
When options such as `returnType`, `output`, or `group` are overridden on a client plugin (`@kubb/plugin-fetch` or `@kubb/plugin-axios`), dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, `@kubb/plugin-mcp`) resolve and follow those per-operation options automatically.
### resolver
Overrides generated file and symbol names. Omitted members keep the plugin's resolver defaults. See [Override a resolver](/docs/5.x/how-to/resolvers) for the `this` context and how a patch layers over the default.
> [!TIP]
> Inside a method `this` is the full resolver, so `this.default.name(name)` reuses the built-in casing.
```typescript [Partial override]
type ResolverClientPatch = {
name?(name: string): string
file?: {
baseName?(params: { name: string; extname: string }): string
path?(params: { baseName: string; output: Output }): string
}
className?(name: string): string
groupName?(name: string): string // → 'PetClient'
propertyName?(name: string): string
}
```
### macros
Rewrites AST nodes before they are printed, without forking the generator. Each [macro](/docs/5.x/how-to/macros) callback (such as `schema` or `operation`) receives the node and a context object, and returns a replacement or `undefined` to leave it as is. Omitted callbacks keep their defaults, and macros run in order, so a later one sees the output of an earlier one.
---
# Kubb Barrel Plugin
Source: https://kubb.dev/plugins/plugin-barrel
---
layout: doc
title: Kubb Barrel Plugin
description: Generates an index.ts barrel for every plugin output and one root
barrel, so you import all generated code from a single entry point. Ships with
Kubb, but only generates barrels once output.barrel is configured.
outline: deep
kind: plugin
id: plugin-barrel
name: Barrel
category: output
type: official
npmPackage: "@kubb/plugin-barrel"
repo: https://github.com/kubb-labs/kubb
docsPath: /plugins/plugin-barrel
featured: true
maintainers:
- name: Stijn Van Hulle
github: stijnvanhulle
compatibility:
kubb: ">=5.0.0"
node: ">=22"
tags:
- barrel
- index
- exports
- output
resources:
documentation: https://kubb.dev/plugins/plugin-barrel
repository: https://github.com/kubb-labs/kubb
issues: https://github.com/kubb-labs/kubb/issues
changelog: https://github.com/kubb-labs/kubb/blob/main/packages/plugin-barrel/CHANGELOG.md
---
# @kubb/plugin-barrel
`@kubb/plugin-barrel` generates `index.ts` files for plugin outputs and a root entry point. Kubb registers it by default. Configure [`output.barrel`](/plugins/plugin-barrel/reference/options#output-barrel) to control exports.
## Installation
::code-group{sync="package-manager"}
```shell [bun]
bun add -d @kubb/plugin-barrel
```
```shell [pnpm]
pnpm add -D @kubb/plugin-barrel
```
```shell [npm]
npm install --save-dev @kubb/plugin-barrel
```
```shell [yarn]
yarn add -D @kubb/plugin-barrel
```
::
## Dependencies
No plugin dependencies. The plugin ships with Kubb and runs by default. Do not add it to `plugins`.
## Example
Set `output.barrel` on the root configuration to enable a root barrel and the default inherited by plugins without their own barrel setting.
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb'
import { pluginTs } from '@kubb/plugin-ts'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen', barrel: { type: 'named' } },
plugins: [pluginTs()],
})
```
Use `'all'` for wildcard exports. Set a plugin's `output.barrel` to `false` to exclude its files, or enable `nested` to reference subdirectory barrels. See [Options](./reference/options).
## Documentation
- [Options](./reference/options)
## See also
- [Changelog](https://github.com/kubb-labs/kubb/blob/main/packages/plugin-barrel/CHANGELOG.md)
---
# Options
Source: https://kubb.dev/plugins/plugin-barrel/reference/options
---
layout: doc
title: Options
description: Configuration options for @kubb/plugin-barrel. Sets the re-export style for the index.ts barrel files Kubb generates.
outline: deep
---
# Options
`pluginBarrel` takes no arguments. You configure it through `output.barrel`, either on `defineConfig` to set the root barrel and the default every plugin inherits, or on a single plugin to override that plugin's barrel.
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| [`output.barrel`](#output-barrel) | `{ type: 'all' \| 'named' } \| false` | `false` | Re-export style for the barrel files |
| [`type`](#type) | `'all' \| 'named'` | Required | Named exports or a wildcard export |
| [`nested`](#nested) | `boolean` | `false` | Write an `index.ts` in every subdirectory |
### output.barrel
Toggle the export style and depth to see the generated barrels.
::barrel-tree
::
Controls how the generated `index.ts` (barrel) re-exports the output. Accepts `{ type: 'named' }` or `{ type: 'all' }`, optionally with `nested: true` (for example `{ type: 'named', nested: true }`) to write an `index.ts` in every subdirectory, or `false` to skip the barrel entirely. Kubb reads the plugin's own `output.barrel` first, falls back to `config.output.barrel` on `defineConfig`, and finally to `false`. Every generator plugin ships a default `output` that sets `barrel: { type: 'named' }`, but passing your own `output` replaces that object wholesale, so repeat `barrel` whenever you set `output` yourself.
The `type` field picks the export style. A plugin's `output.barrel` also accepts `nested`, so the plugin writes an `index.ts` in every subdirectory. The root `output.barrel` has no `nested` field and always stays flat.
Call a plugin with no `output` at all and it uses its own default, which already sets `barrel: { type: 'named' }`. The moment you pass an `output` object of your own, that default is replaced whole, so a plugin configured as `pluginTs({ output: { path: 'types' } })` has no `barrel` of its own and falls back to `config.output.barrel`, which is `false` unless you set it.
Set `barrel: { type: 'named' | 'all' }` on `defineConfig` to enable barrels everywhere: a root barrel, and the default every plugin without its own `output.barrel` inherits. A plugin that sets its own `output.barrel` overrides that inherited value, including back to `false`, which also drops its files from the root barrel.
| Type | Default |
| --- | --- |
| `{ type: 'all' \| 'named', nested?: boolean } \| false` | `false` |
### type
Export style for the barrel files. Required whenever `output.barrel` is set to an object.
- `'named'` re-exports each symbol by name from the file's named exports. Best for tree-shaking and explicit imports.
- `'all'` uses `export *`, a smaller barrel that re-exports everything.
| Type | Default |
| --- | --- |
| `'all' \| 'named'` | Required, no default |
::code-group
```typescript ['named']
// src/gen/index.ts
export { getUser, User } from './api/user'
export { getPost, Post } from './api/post'
export { User } from './api/types/User'
```
```typescript ['all']
// src/gen/index.ts
export * from './api/user'
export * from './api/post'
export * from './api/types/User'
```
::
### nested
Changes what each barrel references. A barrel is written for every directory either way. With `nested: false`, the plugin's top barrel reaches through to the leaf files. With `nested: true`, each barrel re-exports only what sits directly inside its directory, including the subdirectory barrels below it, so callers can import from any depth. This field works on a plugin's `output.barrel` only.
| Type | Default |
| --- | --- |
| `boolean` | `false` |
::code-group
```typescript [nested: false (default) → barrels reference leaf files]
// src/gen/api/index.ts
export * from './user'
export * from './post'
export * from './types/User'
// src/gen/api/types/index.ts
export * from './User'
```
```typescript [nested: true → barrels reference sub-barrels]
// src/gen/api/index.ts re-exports its files and subdirectories
export * from './user'
export * from './post'
export * from './types'
// src/gen/api/types/index.ts re-exports its files
export * from './User'
```
::
---
# Kubb Client Plugin
Source: https://kubb.dev/plugins/plugin-client
---
layout: doc
title: Kubb Client Plugin
description: Generates typed operation functions from your OpenAPI spec that call a client module you own, so you decide the transport, auth, and error handling.
outline: deep
guides:
- id: write-your-client
title: Write your client
- id: authentication
title: Authenticate
- id: calling-operations
title: Call operations
recipes:
- id: validate-requests-and-responses
title: Validate requests and responses
kind: plugin
id: plugin-client
name: Client
category: client
type: official
npmPackage: "@kubb/plugin-client"
repo: https://github.com/kubb-labs/plugins
docsPath: /plugins/plugin-client
featured: false
icon:
light: https://kubb.dev/feature/javascript.svg
maintainers:
- name: Stijn Van Hulle
github: stijnvanhulle
compatibility:
kubb: ">=5.0.0"
node: ">=22"
tags:
- api-client
- http-client
- codegen
- openapi
dependencies:
- plugin-ts
resources:
documentation: https://kubb.dev/plugins/plugin-client
repository: https://github.com/kubb-labs/plugins
issues: https://github.com/kubb-labs/plugins/issues
changelog: https://github.com/kubb-labs/plugins/blob/main/packages/plugin-client/CHANGELOG.md
codesandbox: https://codesandbox.io/p/github/kubb-labs/plugins/main/examples/client
---
# @kubb/plugin-client
`@kubb/plugin-client` turns each OpenAPI operation into a typed async function that calls a client module you write. Kubb generates the operations and their types. It does not choose a transport and does not copy a runtime into your project, so the generated code has no Fetch, Axios, or Kubb dependency.
Use it when [`@kubb/plugin-fetch`](/plugins/plugin-fetch/) or [`@kubb/plugin-axios`](/plugins/plugin-axios/) do more than you want, for example when you sign requests, share one client across many generated packages, or keep an existing HTTP layer.
From your spec, the plugin gives you:
- [Typed functions](/plugins/plugin-client/guide/calling-operations) per operation with grouped `path`, `query`, `headers`, and `body`.
- A `security` list on each call that tells your client which [auth](/plugins/plugin-client/guide/authentication) the operation needs.
- A per-call `client` override, so one function can use a different client.
- Optional [validation](/plugins/plugin-client/recipes/validate-requests-and-responses) schemas from [`@kubb/plugin-zod`](/plugins/plugin-zod/), passed to your client.
Each function takes one grouped options object (`{ path, query, headers, body }`) and returns whatever your `client` returns, typed as your `RequestResult`. See [write your client](/plugins/plugin-client/guide/write-your-client) for the exports the module needs.
## Installation
::code-group{sync="package-manager"}
```shell [bun]
bun add -d @kubb/plugin-client
```
```shell [pnpm]
pnpm add -D @kubb/plugin-client
```
```shell [npm]
npm install --save-dev @kubb/plugin-client
```
```shell [yarn]
yarn add -D @kubb/plugin-client
```
::
## Dependencies
The plugin needs `@kubb/plugin-ts` for the operation types. It needs `@kubb/plugin-zod` only when `validator` is set, and generation stops with an error if `@kubb/plugin-zod` is missing.
- [`@kubb/plugin-ts`](/plugins/plugin-ts/)
- [`@kubb/plugin-zod`](/plugins/plugin-zod/)
> [!IMPORTANT]
> There is no HTTP client to install. The generated functions call the module you point `importPath` at.
## Example
::code-group
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginClient } from '@kubb/plugin-client'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
plugins: [
pluginTs(),
pluginClient({
output: { path: 'clients', mode: 'directory', barrel: { type: 'named' } },
group: { type: 'tag' },
// resolved from each generated file: src/gen/clients// to src/client.ts
importPath: '../../../client',
}),
],
})
```
::
## See also
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
- [Example project](https://github.com/kubb-labs/plugins/tree/main/examples/client)
- [Changelog](https://github.com/kubb-labs/plugins/blob/main/packages/plugin-client/CHANGELOG.md)
---
# Authenticate
Source: https://kubb.dev/plugins/plugin-client/guide/authentication
---
layout: doc
title: Authenticate
description: Add tokens, request signing, or other auth to your client with the security list that @kubb/plugin-client passes, including async credentials.
outline: deep
---
# Authenticate
Auth lives in your `client`. The plugin does not send credentials itself. It tells your client which schemes each operation needs, and your client decides what to do.
## The `security` list
Each generated function passes the operation's security schemes from the spec as `security`:
```typescript
request({
method: 'GET',
url: '/pet/{petId}',
security: [{ type: 'apiKey', name: 'api_key', in: 'header' }, { type: 'oauth2' }],
...config,
})
```
An operation without security requirements passes no `security` field. Use that to skip auth on public endpoints.
## Add a bearer token from an async source
The client can `await` anything before it sends the request. This one asks a registered function for a token, so the token can come from a secrets store or a refresh call:
```typescript [src/client.ts]
type GetToken = () => Promise
let getToken: GetToken | undefined
export function setAuth(resolve: GetToken) {
getToken = resolve
}
export async function client(config: RequestConfig) {
const token = config.security?.length ? await getToken?.() : undefined
const response = await fetch(url, {
method: config.method,
headers: { ...(token ? { Authorization: `Bearer ${token}` } : {}), ...(config.headers as Record) },
// ...
})
// ...
}
```
```typescript [src/index.ts]
import { setAuth } from './client'
setAuth(async () => readTokenFromSecretsStore())
```
## Sign requests or use several auth methods
Read `config.security` and pick per operation, or pass a different `client` on a call. This works for request signing, such as AWS SigV4, and for services that use different credentials:
```typescript
import { createSignedClient } from './clients/iam'
await getPetById({ path: { petId: 1 }, client: createSignedClient() })
```
A client passed on a call replaces the default for that call only.
---
# Call operations
Source: https://kubb.dev/plugins/plugin-client/guide/calling-operations
---
layout: doc
title: Call operations
description: Call the functions Kubb generates from your OpenAPI spec. Pass typed path, query, header, and body parameters, and read the result your client returns.
outline: deep
---
# Call operations
Each generated function takes one grouped options object and returns your client's promise.
```typescript
import { addPet, getPetById } from './gen/clients'
const created = await addPet({ body: { name: 'Odie', photoUrls: [] } })
console.log(created.data, created.response.status)
const pet = await getPetById({ path: { petId: 1n } })
```
The keys are `path`, `query`, `headers`, and `body`. Kubb types each from the spec, so a wrong key or value fails at compile time.
## Errors
`throwOnError` defaults to `true` (see [`throwOnErrorDefault`](/plugins/plugin-client/reference/options#throwonerrordefault)). What that means is up to your client. With the example client, a non-2xx status throws, and `throwOnError: false` returns the error as a value:
```typescript
const result = await getPetById({ path: { petId: 1n }, throwOnError: false })
if (result.error !== undefined) {
console.log('failed', result.response.status)
} else {
console.log('pet', result.data.name)
}
```
## Use a different client for one call
Pass `client` to send one call through another function:
```typescript
await getPetById({ path: { petId: 2n }, client: tenantClient })
```
---
# Write your client
Source: https://kubb.dev/plugins/plugin-client/guide/write-your-client
---
layout: doc
title: Write your client
description: The module that @kubb/plugin-client imports. Export a client function and two types, then add the behavior your app needs.
outline: deep
---
# Write your client
The generated functions import your module through [`importPath`](/plugins/plugin-client/reference/options#importpath). It is the only runtime code in the picture, and you own all of it.
## Required exports
| Export | Kind | Purpose |
| ------ | ---- | ------- |
| `client` | function | Sends the request. Every generated function calls it, unless a call passes its own `client`. |
| `Options` | type | The grouped options object a generated function accepts. |
| `RequestResult` | type | What a generated function resolves to. |
If your spec has server-sent event operations, also export `toEventStream` and the `EventStreamResult` and `SuccessOf` types.
## What a generated function looks like
```typescript
import type { Options, RequestResult } from '../../../client'
import type { GetPetByIdOptions, GetPetByIdResponses } from '../../models/pet/GetPetById'
import { client } from '../../../client'
export function getPetById(
options: Options,
): Promise> {
const { client: request = client, ...config } = options
return request({
method: 'GET',
url: '/pet/{petId}',
security: [{ type: 'apiKey', name: 'api_key', in: 'header' }, { type: 'oauth2' }],
...config,
throwOnError: config.throwOnError ?? true,
}) as Promise>
}
```
Your `client` receives `method`, the `url` template with `{param}` placeholders, `security`, the grouped `path`, `query`, `headers`, and `body`, and `throwOnError`. It returns a promise.
## A minimal client
The client below uses `fetch`. Replace the transport with the HTTP library you use.
```typescript [src/client.ts]
export type RequestConfig = {
method: 'GET' | 'PUT' | 'PATCH' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD'
url: string
baseURL?: string
headers?: object
path?: object
query?: object
body?: unknown
signal?: AbortSignal
throwOnError?: boolean
security?: Array<{ type: string; name?: string; in?: string }>
client?: typeof client
}
type DataShape = { body?: unknown; headers?: unknown; path?: unknown; query?: unknown }
export type Options = Omit &
TData & { throwOnError?: ThrowOnError }
export type RequestResult = ThrowOnError extends true
? { data: TResponses[keyof TResponses]; error: undefined; response: Response }
: { data: TResponses[keyof TResponses]; error: undefined; response: Response } | { data: undefined; error: {}; response: Response }
// Set this to your API host. `new URL()` needs an absolute URL.
export const settings = { baseURL: 'https://petstore3.swagger.io/api/v3' }
export async function client(config: RequestConfig) {
const path: Record = { ...config.path }
const url = new URL((config.baseURL ?? settings.baseURL) + config.url.replace(/\{(\w+)\}/g, (_, key) => encodeURIComponent(String(path[key]))))
const response = await fetch(url, {
method: config.method,
headers: config.headers as Record,
body: config.body === undefined ? undefined : JSON.stringify(config.body),
signal: config.signal,
})
const body = await response.json().catch(() => undefined)
if (response.ok) return { data: body, error: undefined, response }
if (config.throwOnError ?? true) throw new Error(`Request failed with status ${response.status}`)
return { data: undefined, error: body ?? { status: response.status }, response }
}
```
The [example project](https://github.com/kubb-labs/plugins/tree/main/examples/client) has a fuller version with a base URL setter, query handling, and an async auth hook. Type the response map with the status-aware helpers you need. This sketch keeps them simple on purpose.
> [!TIP]
> Start small. Add retries, interceptors, or request signing to `client` when you need them. Kubb does not depend on any of it.
---
# Validate requests and responses
Source: https://kubb.dev/plugins/plugin-client/recipes/validate-requests-and-responses
---
layout: doc
title: Validate requests and responses
description: Pass Zod schemas from @kubb/plugin-zod to your client and validate request and response bodies there. The client runs the schemas, not Kubb.
outline: deep
---
# Validate requests and responses
`@kubb/plugin-client` does not validate on its own. With `validator` set, it passes the schemas to your client, which runs them.
::code-group
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginZod } from '@kubb/plugin-zod'
import { pluginClient } from '@kubb/plugin-client'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
plugins: [
pluginTs(),
pluginZod(),
pluginClient({ importPath: '../../../client', validator: 'zod' }),
],
})
```
::
Each generated call now includes the schemas:
```typescript
request({
method: 'POST',
url: '/pet',
validator: { response: addPetResponseSchema, error: addPetErrorSchema },
...config,
})
```
Add a `validator` field to your `RequestConfig`, then run the schemas in `client`. Zod schemas follow [Standard Schema](https://standardschema.dev), so `~standard.validate` works with any compatible library:
```typescript [src/client.ts]
type Result = { value: unknown; issues?: undefined } | { issues: ReadonlyArray }
type Schema = { '~standard': { validate: (value: unknown) => Result | Promise } }
export type RequestConfig = {
// ...
validator?: { request?: Schema; response?: Schema; error?: Schema }
}
// inside client(), after reading the body
const result = await config.validator?.response?.['~standard'].validate(body)
if (result && 'issues' in result && result.issues) throw new Error('Response validation failed')
const validatedBody = result && 'value' in result ? result.value : body
```
Return `validatedBody` as `data`, so a schema that transforms the value takes effect. Without a response schema, `validatedBody` is the original `body`.
Set `validator: { request: 'zod' }` or `{ response: 'zod' }` to pass one direction only. Without `pluginZod()` in the plugins list, generation stops with an error.
---
# Options
Source: https://kubb.dev/plugins/plugin-client/reference/options
---
layout: doc
title: Options
description: Configuration options for @kubb/plugin-client.
outline: deep
---
# Options
Pass these options to `pluginClient()` to control what it generates and where the files go.
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| [`importPath`](#importpath) | `string` | required | Import specifier of your client module |
| [`output`](#output) | `Output` | `{ path: 'clients', barrel: { type: 'named' } }` | Where the generated files are written and exported |
| [`group`](#group) | `Group` | — | Split output into per-tag or per-path folders |
| [`throwOnErrorDefault`](#throwonerrordefault) | `boolean` | `true` | Default `throwOnError` value passed to your client |
| [`validator`](#validator) | `false \| 'zod' \| { request?: 'zod'; response?: 'zod' }` | `false` | Pass Zod schemas to your client |
| [`include`](#include) | `Array` | — | Keep only operations that match |
| [`exclude`](#exclude) | `Array` | `[]` | Skip operations that match |
| [`override`](#override) | `Array` | `[]` | Apply different options per pattern |
| [`resolver`](#resolver) | `ResolverPatch` | — | Customize generated names and file paths |
| [`macros`](#macros) | `Array` | — | Rewrite AST nodes before printing |
> [!NOTE]
> `sdk`, `returnType`, and `baseURL` from [`@kubb/plugin-fetch`](/plugins/plugin-fetch/reference/options) are not options here. This plugin generates standalone functions that return your client's promise, and your client owns the base URL.
### importPath
Import specifier of your client module. The plugin writes it into every generated import exactly as given, so it must resolve from the generated file. Use a relative path, a package name, or an alias.
```typescript
pluginClient({ importPath: '../../../client' }) // relative to src/gen/clients//
pluginClient({ importPath: '@my-org/api-client' }) // a package
```
With `group: { type: 'tag' }` and `output.path: 'clients'`, a generated file sits in `src/gen/clients//`, so `../../../client` points at `src/client.ts`. Without `group` it is `../../client`.
The module must export `client`, plus the `Options` and `RequestResult` types. See [write your client](/plugins/plugin-client/guide/write-your-client).
### output
Where the plugin writes its generated `.ts` files and how it exports them.
#### output.path
Folder for the plugin's files, resolved against the global `output.path` on `defineConfig` and defaulting to `'clients'`. To write everything to one file, set `output.mode: 'file'` and give `path` a file name with its extension, such as `'clients.ts'`.
#### output.mode
How the plugin consolidates its code into files, either `'file'` or `'directory'`.
- `'file'` writes everything into a single file, so `output.path` must include the extension (see above).
- `'directory'` writes one file per operation under `output.path`.
Leave it unset and Kubb reads `output.path`: a name with an extension means one file, anything else a directory.
#### output.barrel
Toggle the export style and depth to see the generated barrels.
::barrel-tree
::
Controls how the generated `index.ts` (barrel) re-exports the output. Accepts `{ type: 'named' }` or `{ type: 'all' }`, optionally with `nested: true` (for example `{ type: 'named', nested: true }`) to write an `index.ts` in every subdirectory, or `false` to skip the barrel entirely. Kubb reads the plugin's own `output.barrel` first, falls back to `config.output.barrel` on `defineConfig`, and finally to `false`. Every generator plugin ships a default `output` that sets `barrel: { type: 'named' }`, but passing your own `output` replaces that object wholesale, so repeat `barrel` whenever you set `output` yourself.
#### output.banner
Text added to the top of every generated file, such as a license header or `@ts-nocheck` directive. Pass a string, or a function `(meta: BannerMeta) => string` that receives the document info (`title`, `description`, `version`, `baseURL`) and per-file context (`filePath`, `baseName`, `isBarrel`, `isAggregation`), so a directive can skip barrel files.
#### output.footer
Text added to the bottom of every generated file (`string` or `(meta: BannerMeta) => string`), like `banner` but for closing comments. Pair `banner: '/* eslint-disable */'` with `footer: '/* eslint-enable */'` to scope a lint disable to the generated file.
### group
Switch the mode to see where these operations land on disk.
::grouping-diagram
::
Splits generated files into subfolders by the operation's tag or URL path, each under `{output.path}/{groupName}/`. Without `group`, every file lands directly in `output.path`. It applies only to `output.mode: 'directory'`.
> [!IMPORTANT]
> Combining `group` with `output.mode: 'file'` stops the build with a `KUBB_INVALID_PLUGIN_OPTIONS` error.
#### group.type
Property used to assign each operation to a group (`'tag' | 'path'`), required whenever `group` is set. An operation with no tag goes in the `default` group.
- `'tag'` uses the operation's first tag.
- `'path'` uses the first URL segment, such as `pet` for `/pet/{petId}`.
#### group.name
Function `(context: { group: string }) => string` that turns a group key into a folder name. It defaults to the camelCased tag for a `'tag'` group or the first path segment for a `'path'` group, and a `group.name` you pass always wins.
### throwOnErrorDefault
Default for the `throwOnError` field that every generated function passes to your client. It defaults to `true`. A call that sets `throwOnError` itself wins. Your client decides what the flag does: the [example client](/plugins/plugin-client/guide/write-your-client) throws on a non-2xx status when it is `true` and returns the error as a value when it is `false`.
The setting applies to the whole plugin, so you cannot set it in `override`.
### validator
Passes Zod schemas from `@kubb/plugin-zod` to your client on `config.validator`, defaulting to `false`.
- `false` passes nothing.
- `'zod'` passes the response and error schemas.
- `{ request?: 'zod', response?: 'zod' }` opts in per direction.
The plugin does not run the schemas. Your client reads `config.validator` and validates. Add `pluginZod()` to the plugins list when `validator` is set. Generation stops with an error if it is missing. See [validate requests and responses](/plugins/plugin-client/recipes/validate-requests-and-responses).
### include
Generates only the operations and schemas that match at least one entry, and skips the rest. Each entry filters by `tag`, `operationId`, `path`, `method`, `contentType`, or `schemaName`, with a `pattern` that can be a string or a `RegExp`, both matched as a regular expression against the value. A string pattern is compiled with `new RegExp(pattern)`, so it is not an exact match: `pattern: 'pet'` also matches `'petType'` or `'superpet'`.
```typescript [Type definition]
export type Include = {
type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
pattern: string | RegExp
}
```
### exclude
Skips any operation or schema that matches at least one entry, the opposite of `include`. Entries use the same `type` and `pattern` fields as `include`, and when both options match an item, `exclude` wins.
When operations are excluded on a client plugin (`@kubb/plugin-fetch` or `@kubb/plugin-axios`), dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, `@kubb/plugin-mcp`) skip generating hooks or handlers for those operations automatically, without requiring duplicate `exclude` configurations.
### override
Applies different plugin options to operations that match a pattern. Each entry takes the same `type` and `pattern` as `include`, plus an `options` object that accepts any plugin option except `override`, so rules cannot nest. The first matching entry merges onto the plugin defaults, and later entries do not stack.
```typescript [Type definition]
export type Override = {
type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
pattern: string | RegExp
options: Omit, 'override'>
}
```
When options such as `returnType`, `output`, or `group` are overridden on a client plugin (`@kubb/plugin-fetch` or `@kubb/plugin-axios`), dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, `@kubb/plugin-mcp`) resolve and follow those per-operation options automatically.
### resolver
Changes how the plugin names generated files and symbols by accepting a partial patch. Override only the members you want, and anything you omit keeps `resolverClient`. See [Override a resolver](/docs/5.x/how-to/resolvers) for the `this` context and how a patch layers over the default.
> [!TIP]
> Inside a method `this` is the full resolver, so `this.default.name(name)` reuses the built-in casing.
### macros
Rewrites AST nodes before they are printed, without forking the generator. Each [macro](/docs/5.x/how-to/macros) callback (such as `schema` or `operation`) receives the node and a context object, and returns a replacement or `undefined` to leave it as is. Omitted callbacks keep their defaults, and macros run in order, so a later one sees the output of an earlier one.
---
# Kubb Cypress Plugin
Source: https://kubb.dev/plugins/plugin-cypress
---
layout: doc
title: Kubb Cypress Plugin
description: Generates a typed cy.request() wrapper per OpenAPI operation so
your Cypress tests call the API through generated helpers and catch broken
calls at compile time.
outline: deep
kind: plugin
id: plugin-cypress
name: Cypress
category: testing
type: official
npmPackage: "@kubb/plugin-cypress"
repo: https://github.com/kubb-labs/plugins
docsPath: /plugins/plugin-cypress
featured: false
icon:
light: https://kubb.dev/feature/cypress.svg
maintainers:
- name: Stijn Van Hulle
github: stijnvanhulle
compatibility:
kubb: ">=5.0.0"
node: ">=22"
tags:
- cypress
- e2e-testing
- api-testing
- test-generation
- codegen
- openapi
dependencies:
- plugin-ts
resources:
documentation: https://kubb.dev/plugins/plugin-cypress
repository: https://github.com/kubb-labs/plugins
issues: https://github.com/kubb-labs/plugins/issues
changelog: https://github.com/kubb-labs/plugins/blob/main/packages/plugin-cypress/CHANGELOG.md
codesandbox: https://codesandbox.io/p/github/kubb-labs/plugins/main/examples/cypress
---
# @kubb/plugin-cypress
`@kubb/plugin-cypress` generates typed `cy.request()` helpers from OpenAPI operations. Helpers take grouped `{ body, path, query, headers }` parameters and return the response body as `Cypress.Chainable<{Operation}Response>`. Parameter names match the spec.
## Installation
::code-group{sync="package-manager"}
```shell [bun]
bun add -d @kubb/plugin-cypress
```
```shell [pnpm]
pnpm add -D @kubb/plugin-cypress
```
```shell [npm]
npm install --save-dev @kubb/plugin-cypress
```
```shell [yarn]
yarn add -D @kubb/plugin-cypress
```
::
## Dependencies
Add [`pluginTs`](/plugins/plugin-ts/) for request and response types. Generated helpers require Cypress v13 or higher.
## Example
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginCypress } from '@kubb/plugin-cypress'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
plugins: [
pluginTs(),
pluginCypress({ output: { path: 'cypress', mode: 'directory' } }),
],
})
```
```typescript [pet.cy.ts]
import { getPetById } from '../src/gen/cypress/getPetById'
describe('Pet API', () => {
it('returns the pet by id', () => {
getPetById({ path: { petId: 1n } }).then((pet) => {
expect(pet.id).to.eq(1n)
})
})
})
```
## Documentation
- [Options](./reference/options)
## See also
- [Cypress](https://www.cypress.io/)
- [cy.request()](https://docs.cypress.io/api/commands/request)
- [@kubb/plugin-ts](/plugins/plugin-ts/)
- [Changelog](https://github.com/kubb-labs/plugins/blob/main/packages/plugin-cypress/CHANGELOG.md)
---
# Options
Source: https://kubb.dev/plugins/plugin-cypress/reference/options
---
layout: doc
title: Options
description: Configuration options for @kubb/plugin-cypress.
outline: deep
---
# Options
Options for `pluginCypress`, with type and default in the table.
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| [`output`](#output) | `Output` | `{ path: 'cypress', barrel: { type: 'named' } }` | Where the generated files are written and exported |
| [`group`](#group) | `Group` | — | Split output into per-tag or per-path folders |
| [`baseURL`](#baseurl) | `string` | — | Base URL prepended to every request |
| [`include`](#include) | `Array` | — | Keep only operations that match |
| [`exclude`](#exclude) | `Array` | `[]` | Skip operations that match |
| [`override`](#override) | `Array` | `[]` | Apply different options per pattern |
| [`resolver`](#resolver) | `ResolverPatch` | — | Customize generated names and file paths |
| [`macros`](#macros) | `Array` | — | Rewrite AST nodes before printing |
### output
Where the generated `.ts` files are written and how they are exported.
#### output.path
Folder where the plugin writes its files, resolved against the global `output.path` on `defineConfig`.
#### output.mode
How the plugin consolidates its generated code into files.
- `'file'` writes everything into a single file. The `output.path` must include the file extension (for example `'cypress.ts'`).
- `'directory'` writes one file per operation or schema under `output.path`.
Leave it unset and Kubb reads `output.path`: a name with an extension means one file, anything else a directory.
> [!IMPORTANT]
> `group` requires directory output. Kubb infers the mode from `output.path`. Set `mode: 'directory'` to override that inference. Combining `group` with `mode: 'file'` stops generation with `KUBB_INVALID_PLUGIN_OPTIONS`.
#### output.barrel
Toggle the export style and depth to see the generated barrels.
::barrel-tree
::
Controls how the generated `index.ts` (barrel) re-exports the output. Accepts `{ type: 'named' }` or `{ type: 'all' }`, optionally with `nested: true` (for example `{ type: 'named', nested: true }`) to write an `index.ts` in every subdirectory, or `false` to skip the barrel entirely. Kubb reads the plugin's own `output.barrel` first, falls back to `config.output.barrel` on `defineConfig`, and finally to `false`. Every generator plugin ships a default `output` that sets `barrel: { type: 'named' }`, but passing your own `output` replaces that object wholesale, so repeat `barrel` whenever you set `output` yourself.
#### output.banner
Text added to the top of every generated file, such as a license header or `@ts-nocheck` directive. Pass a string, or a function `(meta: BannerMeta) => string` that receives the document info (`title`, `description`, `version`, `baseURL`) and per-file context (`filePath`, `baseName`, `isBarrel`, `isAggregation`), so a directive can skip barrel files.
#### output.footer
Text added to the bottom of every generated file (`string` or `(meta: BannerMeta) => string`), like `banner` but for closing comments. Pair `banner: '/* eslint-disable */'` with `footer: '/* eslint-enable */'` to scope a lint disable to the generated file.
### group
Switch the mode to see where these operations land on disk.
::grouping-diagram
::
Splits generated files into subfolders by the operation's tag or URL path, each under `{output.path}/{groupName}/`. Without `group`, every file lands directly in `output.path`. It applies only to `output.mode: 'directory'`.
> [!IMPORTANT]
> Combining `group` with `output.mode: 'file'` stops the build with a `KUBB_INVALID_PLUGIN_OPTIONS` error.
#### group.type
Property used to assign each operation to a group (`'tag' | 'path'`), required whenever `group` is set. An operation with no tag goes in the `default` group.
- `'tag'` uses the operation's first tag.
- `'path'` uses the first URL segment, such as `pet` for `/pet/{petId}`.
#### group.name
Function that turns a group key into a folder name, used as the subdirectory under `output.path`. By default the key is the camelCased tag, or the first path segment for `'path'` groups.
### baseURL
Base URL prepended to every request in the generated helpers. When omitted, no host is prepended and each helper uses the operation's relative path from the spec. Set it to point the helpers at a different environment, such as staging or production.
```typescript [baseURL: 'https://staging.petstore.dev']
export function showPetById(
{ path }: ShowPetByIdOptions,
options: Partial = {},
): Cypress.Chainable {
return cy
.request({
method: 'GET',
url: `https://staging.petstore.dev/pets/${path.petId}`,
...options,
})
.then((res) => res.body)
}
```
```typescript [kubb.config.ts]
import { pluginCypress } from '@kubb/plugin-cypress'
pluginCypress({ baseURL: 'https://staging.example.com' })
```
Keep `pluginTs()` in the configuration. The host is emitted into each helper's URL. No runtime host setup is needed.
### include
Generates only the operations and schemas that match at least one entry, and skips the rest. Each entry filters by `tag`, `operationId`, `path`, `method`, `contentType`, or `schemaName`, with a `pattern` that can be a string or a `RegExp`, both matched as a regular expression against the value. A string pattern is compiled with `new RegExp(pattern)`, so it is not an exact match: `pattern: 'pet'` also matches `'petType'` or `'superpet'`.
```typescript [Type definition]
export type Include = {
type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
pattern: string | RegExp
}
```
### exclude
Skips any operation or schema that matches at least one entry, the opposite of `include`. Entries use the same `type` and `pattern` fields as `include`, and when both options match an item, `exclude` wins.
When operations are excluded on a client plugin (`@kubb/plugin-fetch` or `@kubb/plugin-axios`), dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, `@kubb/plugin-mcp`) skip generating hooks or handlers for those operations automatically, without requiring duplicate `exclude` configurations.
### override
Applies different plugin options to operations that match a pattern. Each entry takes the same `type` and `pattern` as `include`, plus an `options` object that accepts any plugin option except `override`, so rules cannot nest. The first matching entry merges onto the plugin defaults, and later entries do not stack.
```typescript [Type definition]
export type Override = {
type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
pattern: string | RegExp
options: Omit, 'override'>
}
```
When options such as `returnType`, `output`, or `group` are overridden on a client plugin (`@kubb/plugin-fetch` or `@kubb/plugin-axios`), dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, `@kubb/plugin-mcp`) resolve and follow those per-operation options automatically.
### resolver
Overrides generated file and symbol names. Omitted members keep the plugin's resolver defaults. See [Override a resolver](/docs/5.x/how-to/resolvers) for the `this` context and how a patch layers over the default.
> [!TIP]
> Inside a method `this` is the full resolver, so `this.default.name(name)` reuses the built-in casing.
```typescript [Partial override]
type ResolverCypressPatch = {
name?(name: string): string
file?: {
baseName?(params: { name: string; extname: string }): string
path?(params: { baseName: string; output: Output }): string
}
// no extra namespaces
}
```
### macros
Rewrites AST nodes before they are printed, without forking the generator. Each [macro](/docs/5.x/how-to/macros) callback (such as `schema` or `operation`) receives the node and a context object, and returns a replacement or `undefined` to leave it as is. Omitted callbacks keep their defaults, and macros run in order, so a later one sees the output of an earlier one.
---
# Kubb Faker Plugin
Source: https://kubb.dev/plugins/plugin-faker
---
layout: doc
title: Kubb Faker Plugin
description: Generates a Faker.js mock-data factory for every schema in your
OpenAPI spec. Use the factories in tests, Storybook, and local development
without a running backend.
outline: deep
kind: plugin
id: plugin-faker
name: Faker
category: mocks
type: official
npmPackage: "@kubb/plugin-faker"
repo: https://github.com/kubb-labs/plugins
docsPath: /plugins/plugin-faker
featured: false
icon:
light: https://kubb.dev/feature/faker.svg
maintainers:
- name: Stijn Van Hulle
github: stijnvanhulle
compatibility:
kubb: ">=5.0.0"
node: ">=22"
tags:
- faker
- mock-data
- mocks
- fixtures
- testing
- codegen
- openapi
dependencies:
- plugin-ts
resources:
documentation: https://kubb.dev/plugins/plugin-faker
repository: https://github.com/kubb-labs/plugins
issues: https://github.com/kubb-labs/plugins/issues
changelog: https://github.com/kubb-labs/plugins/blob/main/packages/plugin-faker/CHANGELOG.md
codesandbox: https://codesandbox.io/p/github/kubb-labs/plugins/main/examples/faker
---
# @kubb/plugin-faker
`@kubb/plugin-faker` generates typed mock-data factories from OpenAPI schemas. Pass partial data to a factory to override generated values.
## Installation
::code-group{sync="package-manager"}
```shell [bun]
bun add -d @kubb/plugin-faker
```
```shell [pnpm]
pnpm add -D @kubb/plugin-faker
```
```shell [npm]
npm install --save-dev @kubb/plugin-faker
```
```shell [yarn]
yarn add -D @kubb/plugin-faker
```
::
## Dependencies
Add [`pluginTs`](/plugins/plugin-ts/) for factory return types. Generated factories require `@faker-js/faker` v9 or higher.
## Example
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginFaker } from '@kubb/plugin-faker'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
plugins: [
pluginTs(),
pluginFaker({ seed: [100] }),
],
})
```
## Documentation
- [Options](./reference/options)
## See also
- [Faker.js](https://fakerjs.dev/)
- [@kubb/plugin-ts](/plugins/plugin-ts/)
- [@kubb/plugin-msw](/plugins/plugin-msw/)
- [Changelog](https://github.com/kubb-labs/plugins/blob/main/packages/plugin-faker/CHANGELOG.md)
---
# Options
Source: https://kubb.dev/plugins/plugin-faker/reference/options
---
layout: doc
title: Options
description: Configuration options for @kubb/plugin-faker.
outline: deep
---
# Options
Configure `@kubb/plugin-faker` by passing these options to `pluginFaker()`, all of them optional.
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| [`output`](#output) | `Output` | `{ path: 'mocks', barrel: { type: 'named' } }` | Where the generated files are written and exported |
| [`group`](#group) | `Group` | — | Split output into per-tag or per-path folders |
| [`dateParser`](#dateparser) | `'faker' \| 'dayjs' \| 'moment' \| string` | `'faker'` | Library that formats string date and time fields |
| [`regexGenerator`](#regexgenerator) | `'faker' \| 'randexp'` | `'faker'` | Library that turns a regex `pattern` into a string |
| [`locale`](#locale) | `string` | `'en'` | Faker locale code for the generated values |
| [`seed`](#seed) | `number \| number[]` | — | Value passed to `faker.seed(...)` for deterministic output |
| [`include`](#include) | `Array` | — | Keep only operations that match |
| [`exclude`](#exclude) | `Array` | `[]` | Skip operations that match |
| [`override`](#override) | `Array` | `[]` | Apply different options per pattern |
| [`resolver`](#resolver) | `ResolverPatch` | — | Customize generated names and file paths |
| [`macros`](#macros) | `Array` | — | Rewrite AST nodes before printing |
| [`printer`](#printer) | `{ nodes?: PrinterFakerNodes }` | — | Replace the handler for a schema type |
### output
Where the generated `.ts` files are written and how they are exported.
#### output.path
Folder where the plugin writes its files, resolved against the global `output.path` on `defineConfig`, and defaulting to `'mocks'`. To write everything to one file, set `output.mode: 'file'` and give `path` a file name with its extension, such as `'mocks.ts'`.
#### output.mode
How the plugin consolidates generated code. `'file'` writes everything into a single file, where `output.path` must include the extension such as `'mocks.ts'`. `'directory'` writes one file per operation or schema under `output.path`. Leave it unset and Kubb reads `output.path`: a name with an extension means one file, anything else a directory.
> [!IMPORTANT]
> `group` requires directory output. Kubb infers the mode from `output.path`. Set `mode: 'directory'` to override that inference. Combining `group` with `mode: 'file'` stops generation with `KUBB_INVALID_PLUGIN_OPTIONS`.
#### output.barrel
Toggle the export style and depth to see the generated barrels.
::barrel-tree
::
Controls how the generated `index.ts` (barrel) re-exports the output. Accepts `{ type: 'named' }` or `{ type: 'all' }`, optionally with `nested: true` (for example `{ type: 'named', nested: true }`) to write an `index.ts` in every subdirectory, or `false` to skip the barrel entirely. Kubb reads the plugin's own `output.barrel` first, falls back to `config.output.barrel` on `defineConfig`, and finally to `false`. Every generator plugin ships a default `output` that sets `barrel: { type: 'named' }`, but passing your own `output` replaces that object wholesale, so repeat `barrel` whenever you set `output` yourself.
#### output.banner
Text added to the top of every generated file, such as a license header or `@ts-nocheck` directive. Pass a string, or a function `(meta: BannerMeta) => string` that receives the document info (`title`, `description`, `version`, `baseURL`) and per-file context (`filePath`, `baseName`, `isBarrel`, `isAggregation`), so a directive can skip barrel files.
#### output.footer
Text added to the bottom of every generated file (`string` or `(meta: BannerMeta) => string`), like `banner` but for closing comments. Pair `banner: '/* eslint-disable */'` with `footer: '/* eslint-enable */'` to scope a lint disable to the generated file.
### group
Switch the mode to see where these operations land on disk.
::grouping-diagram
::
Splits generated files into subfolders by the operation's tag or URL path, each under `{output.path}/{groupName}/`. Without `group`, every file lands directly in `output.path`. It applies only to `output.mode: 'directory'`.
> [!IMPORTANT]
> Combining `group` with `output.mode: 'file'` stops the build with a `KUBB_INVALID_PLUGIN_OPTIONS` error.
#### group.type
Property used to assign each operation to a group (`'tag' | 'path'`), required whenever `group` is set. An operation with no tag goes in the `default` group.
- `'tag'` uses the operation's first tag.
- `'path'` uses the first URL segment, such as `pet` for `/pet/{petId}`.
#### group.name
Function `(context: { group: string }) => string` that turns a group key into the subdirectory name and the suffix for aggregate files. It defaults to `camelCase(group)` for tag groups, and for `type: 'path'` groups uses the path segment as-is.
### dateParser
Library used to format `date` and `time` fields represented as strings. Pick a value other than `'faker'` when your project already uses a date library. Any library exporting a default function works, and Kubb adds the import for you.
A string `date` field renders differently per parser:
::code-group
```typescript ['faker' (default)]
faker.date.anytime().toISOString().substring(0, 10)
```
```typescript ['dayjs']
dayjs(faker.date.anytime()).format('YYYY-MM-DD')
```
```typescript ['moment']
moment(faker.date.anytime()).format('YYYY-MM-DD')
```
::
```typescript [kubb.config.ts]
import { pluginFaker } from '@kubb/plugin-faker'
pluginFaker({ dateParser: 'dayjs' })
```
Install `dayjs` in the consuming app. Generated `date` and `time` strings use `YYYY-MM-DD` and `HH:mm:ss`. `date-time` values keep the ISO string.
### regexGenerator
Library used to generate strings that satisfy a regex `pattern` keyword in the spec. The default `'faker'` emits `faker.helpers.fromRegExp(pattern)` and needs no extra dependency. `'randexp'` emits `new RandExp(pattern).gen()`, which supports a wider regex grammar but adds the `randexp` runtime dependency.
### locale
Faker locale code. It switches the named import to `fakerXX` from `@faker-js/faker`, so generated values reflect the target region. The default `'en'` imports `fakerEN`, `'de'` imports `fakerDE`, and `'de_AT'` imports `fakerDE_AT`. See [Faker.js localization](https://fakerjs.dev/api/localization.html) for all locale codes.
```typescript [kubb.config.ts]
import { pluginFaker } from '@kubb/plugin-faker'
pluginFaker({ locale: 'de' })
```
### seed
Value passed to `faker.seed(...)` and emitted at the top of each generated factory, giving deterministic output across runs for snapshot tests and reproducible local data. Pass a single number or an array of numbers.
```typescript [kubb.config.ts]
import { pluginFaker } from '@kubb/plugin-faker'
pluginFaker({ seed: [100] })
```
Factories still accept overrides, such as `createPet({ name: 'Rex' })`.
### include
Generates only the operations and schemas that match at least one entry, and skips the rest. Each entry filters by `tag`, `operationId`, `path`, `method`, `contentType`, or `schemaName`, with a `pattern` that can be a string or a `RegExp`, both matched as a regular expression against the value. A string pattern is compiled with `new RegExp(pattern)`, so it is not an exact match: `pattern: 'pet'` also matches `'petType'` or `'superpet'`.
```typescript [Type definition]
export type Include = {
type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
pattern: string | RegExp
}
```
### exclude
Skips any operation or schema that matches at least one entry, the opposite of `include`. Entries use the same `type` and `pattern` fields as `include`, and when both options match an item, `exclude` wins.
When operations are excluded on a client plugin (`@kubb/plugin-fetch` or `@kubb/plugin-axios`), dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, `@kubb/plugin-mcp`) skip generating hooks or handlers for those operations automatically, without requiring duplicate `exclude` configurations.
### override
Applies different plugin options to operations that match a pattern. Each entry takes the same `type` and `pattern` as `include`, plus an `options` object that accepts any plugin option except `override`, so rules cannot nest. The first matching entry merges onto the plugin defaults, and later entries do not stack.
```typescript [Type definition]
export type Override = {
type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
pattern: string | RegExp
options: Omit, 'override'>
}
```
When options such as `returnType`, `output`, or `group` are overridden on a client plugin (`@kubb/plugin-fetch` or `@kubb/plugin-axios`), dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, `@kubb/plugin-mcp`) resolve and follow those per-operation options automatically.
### resolver
Overrides generated file and symbol names. Omitted members keep the plugin's resolver defaults. See [Override a resolver](/docs/5.x/how-to/resolvers) for the `this` context and how a patch layers over the default.
> [!TIP]
> Inside a method `this` is the full resolver, so `this.default.name(name)` reuses the built-in casing.
```typescript [Partial override]
type ResolverFakerPatch = {
name?(name: string): string
file?: {
baseName?(params: { name: string; extname: string }): string
path?(params: { baseName: string; output: Output }): string
}
param?: {
name?(node: OperationNode, param: ParameterNode): string // → 'showPetByIdPathPetId'
path?(node: OperationNode, param: ParameterNode): string // → 'createShowPetByIdPath'
query?(node: OperationNode, param: ParameterNode): string // → 'createListPetsQuery'
headers?(node: OperationNode, param: ParameterNode): string // → 'createDeletePetHeaders'
}
response?: {
status?(node: OperationNode, statusCode: StatusCode): string // → 'listPetsStatus200'
body?(node: OperationNode): string // → 'createPetsBody'
response?(node: OperationNode): string // → 'listPetsResponse'
responses?(node: OperationNode): string // → 'listPetsResponses'
}
}
```
### macros
Rewrites AST nodes before they are printed, without forking the generator. Each [macro](/docs/5.x/how-to/macros) callback (such as `schema` or `operation`) receives the node and a context object, and returns a replacement or `undefined` to leave it as is. Omitted callbacks keep their defaults, and macros run in order, so a later one sees the output of an earlier one.
### printer
Replaces the Faker node handler for a specific schema type, such as `'integer'`, `'date'`, or `'string'`. Each handler returns the Faker expression as a string. Use `this.transform` to recurse into nested nodes and `this.options` to read printer options. The [printer guide](/docs/5.x/how-to/printers) covers how overrides compose with macros.
```typescript twoslash [Map integer schemas to a float]
import { pluginFaker } from '@kubb/plugin-faker'
pluginFaker({
printer: {
nodes: {
integer() {
return 'faker.number.float()'
},
},
},
})
```
---
# Kubb Fetch Plugin
Source: https://kubb.dev/plugins/plugin-fetch
---
layout: doc
title: Kubb Fetch Plugin
description: Generates a type-safe Fetch API client from your OpenAPI spec, one
async function per operation, so each call stays in sync with the API.
outline: deep
guides:
- id: authentication
title: Authenticate
- id: base-url
title: Set base URL
- id: calling-operations
title: Call operations
- id: error-handling
title: Handle errors
- id: interceptors
title: Add interceptors
- id: serialization
title: Configure serialization
- id: server-sent-events
title: Server-sent events
- id: transport
title: Use custom transport
kind: plugin
id: plugin-fetch
name: Fetch
category: client
type: official
npmPackage: "@kubb/plugin-fetch"
repo: https://github.com/kubb-labs/plugins
docsPath: /plugins/plugin-fetch
featured: true
icon:
light: https://kubb.dev/feature/javascript.svg
maintainers:
- name: Stijn Van Hulle
github: stijnvanhulle
compatibility:
kubb: ">=5.0.0"
node: ">=22"
tags:
- api-client
- fetch
- http-client
- codegen
- openapi
- validator
dependencies:
- plugin-ts
resources:
documentation: https://kubb.dev/plugins/plugin-fetch
repository: https://github.com/kubb-labs/plugins
issues: https://github.com/kubb-labs/plugins/issues
changelog: https://github.com/kubb-labs/plugins/blob/main/packages/plugin-fetch/CHANGELOG.md
codesandbox: https://codesandbox.io/p/github/kubb-labs/plugins/main/examples/fetch
---
# @kubb/plugin-fetch
`@kubb/plugin-fetch` generates a typed async function for each OpenAPI operation using the native [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). Calls accept grouped request parameters and return a status-keyed result.
## Installation
::code-group{sync="package-manager"}
```shell [bun]
bun add -d @kubb/plugin-fetch
```
```shell [pnpm]
pnpm add -D @kubb/plugin-fetch
```
```shell [npm]
npm install --save-dev @kubb/plugin-fetch
```
```shell [yarn]
yarn add -D @kubb/plugin-fetch
```
::
## Dependencies
Add [`pluginTs`](/plugins/plugin-ts/) or [`pluginZod`](/plugins/plugin-zod/) with `inferred: true` for operation types. `pluginTs` takes precedence when both are configured. Validation also requires `pluginZod`. The generated client uses native `fetch`.
## Example
```typescript twoslash [kubb.config.ts]
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({ baseURL: 'https://petstore.swagger.io/v2' }),
],
})
```
## Documentation
- [Options](./reference/options)
- [Authenticate](./guide/authentication)
- [Set base URL](./guide/base-url)
- [Call operations](./guide/calling-operations)
- [Handle errors](./guide/error-handling)
- [Add interceptors](./guide/interceptors)
- [Serialize parameters](./guide/serialization)
- [Server-sent events](./guide/server-sent-events)
- [Use custom transport](./guide/transport)
## See also
- [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
- [`@kubb/plugin-ts`](/plugins/plugin-ts/)
- [Changelog](https://github.com/kubb-labs/plugins/blob/main/packages/plugin-fetch/CHANGELOG.md)
---
# Authenticate your API client
Source: https://kubb.dev/plugins/plugin-fetch/guide/authentication
---
layout: doc
title: Authenticate your API client
description: Add authentication to the client Kubb generates. Resolve bearer, basic, and apiKey credentials from your OpenAPI security schemes with one auth callback.
outline: deep
---
# Authenticate your API client
Set an `auth` resolver on the generated Fetch or Axios client. Operations use the security schemes declared in your OpenAPI spec. Requests remain unauthenticated until you provide credentials.
## Set the auth resolver
Set the resolver for all secured operations:
```typescript
import { client } from './gen/.kubb/client'
client.setConfig({
auth: () => localStorage.getItem('token') ?? undefined,
})
```
The resolver is a token string, or a function (which can be async) that returns one or `undefined` to skip a scheme. When an operation accepts more than one scheme, the runtime tries each in turn until one hands back a token.
## Return the right token per scheme
Use the scheme passed to the resolver to select a credential:
```typescript
type Auth = {
type: 'http' | 'apiKey' | 'oauth2' | 'openIdConnect'
scheme?: 'bearer' | 'basic'
name?: string
in?: 'header' | 'query' | 'cookie'
}
```
The scheme decides where the runtime puts the token:
| Scheme in the spec | `Auth` object | Where the token goes |
| ------------------ | ---------------------------------------- | ------------------------------- |
| `http` bearer | `{ type: 'http', scheme: 'bearer' }` | `Authorization: Bearer ` |
| `http` basic | `{ type: 'http', scheme: 'basic' }` | `Authorization: Basic ` |
| `apiKey` header | `{ type: 'apiKey', name, in: 'header' }` | request header named `name` |
| `apiKey` query | `{ type: 'apiKey', name, in: 'query' }` | query parameter named `name` |
| `apiKey` cookie | `{ type: 'apiKey', name, in: 'cookie' }` | `Cookie` header |
| `oauth2` | `{ type: 'oauth2' }` | `Authorization: Bearer ` |
| `openIdConnect` | `{ type: 'openIdConnect' }` | `Authorization: Bearer ` |
> [!NOTE]
> For basic auth, return the raw `username:password` string. The runtime base64-encodes it and writes the `Basic` prefix, so you never build the header yourself.
When a spec mixes schemes, branch on the `Auth` object and return the matching credential:
```typescript
client.setConfig({
auth: (auth) => (auth.type === 'apiKey' ? apiKey : accessToken),
})
```
## Use a separate client per environment
To give each environment its own token, such as one per tenant in a multi-tenant app, build an isolated client with `createClient`:
```typescript
import { createClient } from './gen/.kubb/client'
const tenant = createClient({
baseURL: 'https://tenant.example.com',
auth: () => tenantToken,
})
const { data } = await getPetById({ path: { petId: 1 }, client: tenant })
```
To override the client for one request, pass `auth` on that single call, which suits a one-off token refresh. An explicit `headers` value you set on a call always wins over the resolved token.
For authentication outside OpenAPI security schemes, use an async [request interceptor](/plugins/plugin-fetch/guide/interceptors) to sign requests or supply custom headers.
## See also
- [Interceptors](/plugins/plugin-fetch/guide/interceptors)
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
- [OpenAPI security scheme object](https://spec.openapis.org/oas/v3.1.0#security-scheme-object)
---
# Set your own baseURL
Source: https://kubb.dev/plugins/plugin-fetch/guide/base-url
---
layout: doc
title: Set your own baseURL
description: Set up base URLs for API clients generated by Kubb. Environment-specific configuration and dynamic base URLs.
outline: deep
---
# Set the API base URL
Set `baseURL` on the client plugin or generated client. OpenAPI server URLs do not configure the client automatically. The adapter’s server option only sets document metadata.
## Use the baseURL option
Pass `baseURL` to the client plugin to set the generated default. Include `pluginTs`, which the client requires.
::code-group
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb/config'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginAxios } from '@kubb/plugin-axios'
export default defineConfig({
input: './petStore.yaml',
output: {
path: './src/gen',
},
plugins: [
pluginTs(),
pluginAxios({ baseURL: 'https://localhost:8080/api/v1' }),
],
})
```
::
A value containing a `${...}` interpolation stays dynamic. The plugin emits it as a template literal in the generated client config, so `baseURL: '${process.env.API_URL}'` reads the environment variable when the app runs instead of baking in the build-time value.
## Set it at runtime
Use `client.setConfig` to configure all operations at startup:
```typescript
import { client } from './gen/.kubb/client'
client.setConfig({ baseURL: import.meta.env.VITE_API_URL })
```
Use `createClient` for an isolated client, then pass it to an operation:
```typescript
import { createClient } from './gen/.kubb/client'
import { getPetById } from './gen/clients/getPetById'
const staging = createClient({ baseURL: 'https://staging.petstore.swagger.io/v2' })
const { data } = await getPetById({ path: { petId: 1 }, client: staging })
```
Pass `baseURL` on a single call to override the client for that one request:
```typescript
import { getPetById } from './gen/clients/getPetById'
const { data } = await getPetById({ path: { petId: 1 }, baseURL: 'https://localhost:3000' })
```
A `baseURL` set on the call wins over `createClient`, which wins over `setConfig`, which wins over the build-time value.
## See also
- [Custom transport](/plugins/plugin-fetch/guide/transport)
- [Authentication](/plugins/plugin-fetch/guide/authentication)
- [Interceptors](/plugins/plugin-fetch/guide/interceptors)
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
---
# Call operations
Source: https://kubb.dev/plugins/plugin-fetch/guide/calling-operations
---
layout: doc
title: Call operations
description: Call the functions Kubb generates from your OpenAPI spec. Pass typed path, query, header, cookie, and body parameters, then read the status, data, and native response off one result.
outline: deep
---
# 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.
```typescript
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`:
```typescript
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](/plugins/plugin-fetch/guide/serialization).
## Read the result
A resolved call returns a `RequestResult` discriminated by the numeric `status`:
```typescript
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:
```typescript
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:
```typescript
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](/plugins/plugin-fetch/guide/error-handling).
## Unwrap the success body
Call `.unwrap()` to return the success body. Awaiting the operation directly returns the full result.
```typescript
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:
```typescript
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`:
```typescript
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](/plugins/plugin-fetch/guide/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:
```typescript
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:
```typescript
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](/plugins/plugin-fetch/guide/serialization).
## Reuse one configuration
Every generated function imports a shared `client`. Call `setConfig` once at startup and every
call picks up the change:
```typescript
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:
```typescript
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](/plugins/plugin-fetch/reference/options#validator) for request validation and per-direction settings.
```typescript [kubb.config.ts]
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:
```typescript
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:
```typescript [src/app.ts]
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](/plugins/plugin-fetch/guide/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:
```typescript
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
```
## See also
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
- [Error handling](/plugins/plugin-fetch/guide/error-handling)
- [Serialization](/plugins/plugin-fetch/guide/serialization)
- [Server-sent events](/plugins/plugin-fetch/guide/server-sent-events)
- [Set your own baseURL](/plugins/plugin-fetch/guide/base-url)
- [Authentication](/plugins/plugin-fetch/guide/authentication)
- [Custom transport](/plugins/plugin-fetch/guide/transport)
---
# Error handling
Source: https://kubb.dev/plugins/plugin-fetch/guide/error-handling
---
layout: doc
title: Error handling
description: Handle failures from the client Kubb generates. Catch a ResponseError, switch to a returned error result with throwOnError, and tell a network failure apart from a 4xx or 5xx response.
outline: deep
---
# Error handling
Generated Fetch and Axios clients throw on non-2xx responses by default. Set the generated default on the client plugin. Individual operations can override it:
```typescript
import { getPetById } from './gen/clients/getPetById'
import { ResponseError } from './gen/.kubb/client'
try {
const { data } = await getPetById({ path: { petId: 1 } })
console.info(data.name)
} catch (error) {
if (ResponseError.is(error)) {
console.error(error.status) // 404
console.error(error.data) // the parsed error body
}
}
```
A `ResponseError` includes the HTTP method and URL (without query parameters, keeping credentials safe) in its `message`, e.g. `GET https://api.example.com/pets/1 failed with status 404 Not Found`.
The error exposes these fields:
```typescript
class ResponseError extends Error {
static is(error: unknown): error is ResponseError
data: TError // the parsed error body
status: number
statusText: string
contentType: string | undefined
request: Request // AxiosRequestConfig on plugin-axios
response: Response // AxiosResponse on plugin-axios
}
```
Use `ResponseError.is(error)` to narrow errors across generated packages, which each bundle their own class. It checks the error name.
## Return the error instead
Pass `throwOnError: false` and the call resolves for every documented status. The result is a
discriminated union: `data` is set on success and `error` on failure, so branch on `status` or
check which field is present.
```typescript
const result = await getPetById({ path: { petId: 1 }, throwOnError: false })
if (result.error) {
console.error(result.status, result.error)
} else {
console.info(result.data.name)
}
```
Check `status` to narrow responses to a specific documented status code.
## Set the generated default
Set `throwOnErrorDefault` on `@kubb/plugin-fetch` or `@kubb/plugin-axios` to choose how generated
operations handle non-2xx responses by default. Each call can still override that setting with
`throwOnError`:
```typescript
import { pluginFetch } from '@kubb/plugin-fetch'
pluginFetch({ throwOnErrorDefault: false })
// resolves with an error result
const list = await searchPets({ query: { status: 'available' } })
// opt one call back into throwing
const pet = await getPetById({ path: { petId: 1 }, throwOnError: true })
```
The generated operation passes its selected value to the client runtime, so setting
`throwOnError` with `client.setConfig` does not change this default. Configure the plugin to
change the generated default.
## Network failures still throw
`throwOnError` governs the response status, not the send: a dropped connection, DNS failure, or
aborted `AbortSignal` rejects regardless of the setting. A `ResponseError` means the server
answered with a non-2xx, and anything else means the request never completed.
```typescript
try {
const { data } = await getPetById({ path: { petId: 1 }, throwOnError: false })
use(data)
} catch (error) {
// not a ResponseError: the request never completed
console.error('request failed to send', error)
}
```
Pass an `AbortSignal` to cancel a request. The call rejects with the abort reason.
## Validation failures
When you turn on the [`validator`](/plugins/plugin-fetch/guide/serialization) option, a body
that does not match its schema throws a `ParseError` instead of returning. It carries the raw
`issues` from the schema, so the same handling works across Zod, valibot, and arktype:
```typescript
import { ParseError } from './gen/.kubb/standardSchema'
try {
const { data } = await getPetById({ path: { petId: 1 } })
use(data)
} catch (error) {
if (error instanceof ParseError) {
console.error(error.issues) // [{ message, path }]
}
}
```
A `ParseError` reports schema validation issues. A `ResponseError` reports a non-2xx status. On the non-throwing path, configured error schemas validate the error body separately from success schemas.
Calling `.unwrap()` on a `throwOnError: false` call turns that same `error` into a rejection, so a
`try`/`catch` works there too. See
[unwrap the success body](/plugins/plugin-fetch/guide/calling-operations#unwrap-the-success-body).
## See also
- [Call operations](/plugins/plugin-fetch/guide/calling-operations)
- [Serialization](/plugins/plugin-fetch/guide/serialization)
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
- [Custom transport](/plugins/plugin-fetch/guide/transport)
---
# Interceptors
Source: https://kubb.dev/plugins/plugin-fetch/guide/interceptors
---
layout: doc
title: Interceptors
description: Run code on every call the Kubb client makes. Add or rewrite headers before the send, observe or transform the response, react to errors, and register, replace, or remove handlers at runtime.
outline: deep
---
# Interceptors
Register handlers on `client.interceptors.request`, `.response`, or `.error` to apply behavior across calls. Fetch handlers receive resolved request/result objects. Axios handlers receive native Axios objects.
## Run before the send
Request handlers run after URL construction, serialization, and authentication. Return the modified request:
::code-group
```typescript [Fetch]
import { client } from './gen/.kubb/client'
client.interceptors.request.use((request) => {
request.headers['X-Request-ID'] = crypto.randomUUID()
return request
})
```
```typescript [Axios]
import { client } from './gen/.kubb/client'
client.interceptors.request.use((request) => {
request.headers.set('X-Request-ID', crypto.randomUUID())
return request
})
```
::
On fetch the handler receives a `ResolvedRequest` (`url`, `method`, `headers`, `body`, `signal`,
`credentials`, `options`, `responseType`). On axios it receives an `InternalAxiosRequestConfig`,
so headers go through `AxiosHeaders` and the body is on `data`.
## Run after the send
Response handlers run before calls resolve. Fetch handlers run before the status split and deserialization, so `data` is the raw body.
::code-group
```typescript [Fetch]
client.interceptors.response.use((result) => {
console.info(`${result.status} ${result.request.url}`)
return result
})
```
```typescript [Axios]
client.interceptors.response.use((response) => {
console.info(`${response.status} ${response.config.url}`)
return response
})
```
::
The fetch handler receives a `TransportResult` (`data`, `status`, `statusText`, `headers`,
`contentType`, `request`, `response`), the axios handler an `AxiosResponse`.
## React to an error
Use an error handler to react to HTTP errors on the throwing path:
::code-group
```typescript [Fetch]
client.interceptors.error.use((error) => {
if (error.status === 401) scheduleTokenRefresh()
return error
})
```
```typescript [Axios]
client.interceptors.error.use((error) => {
if (error.response?.status === 401) scheduleTokenRefresh()
return error
})
```
::
The fetch handler receives the `ResponseError`, the axios handler an `AxiosError`, and this
channel only fires on the throw path. When you read with `throwOnError: false`, a documented
non-2xx response resolves and no error interceptor runs, so inspect the returned `error` on the
result instead. A transport failure (no response at all) still throws on both, but only axios
fires the error channel for it, since the channel is wired directly into its own rejection
handling, while fetch has no result to hand the interceptor stacks. See
[error handling](/plugins/plugin-fetch/guide/error-handling) for that path.
## Add, replace, and remove handlers
`use` registers a handler and returns an id. Pass that id to `eject` to remove it, or to `update`
to swap its function in place. Response and error handlers run in the order they were registered,
and so do request handlers on `plugin-fetch`, but `plugin-axios` delegates to axios, which runs
them in reverse registration order.
```typescript
const id = client.interceptors.request.use((request) => request)
client.interceptors.request.update(id, (request) => {
request.headers['X-Trace'] = 'on'
return request
})
client.interceptors.request.eject(id)
```
## Where to set them
Register handlers on the shared `client` for all operations, or on a separate `createClient` instance for isolated calls. Interceptors are instance-level settings.
On `@kubb/plugin-axios`, interceptors registered through `client.interceptors` also follow a custom
transport set later with `client.setConfig({ transport })`. When the transport changes, handlers
detach from the old instance and attach to the new one, and their IDs stay valid for `eject` and
`update`. Clearing the transport (`client.setConfig({ transport: undefined })`) moves them back to the
client's base instance. A per-call `transport` still bypasses client interceptors.
## See also
- [Call operations](/plugins/plugin-fetch/guide/calling-operations)
- [Error handling](/plugins/plugin-fetch/guide/error-handling)
- [Authentication](/plugins/plugin-fetch/guide/authentication)
- [Custom transport](/plugins/plugin-fetch/guide/transport)
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
---
# Configure serialization
Source: https://kubb.dev/plugins/plugin-fetch/guide/serialization
---
layout: doc
title: Configure serialization
description: Override parameter serializers, add body codecs, and configure response decoding and validation.
outline: deep
---
# Configure serialization
Fetch and Axios clients encode parameters using OpenAPI `style` and `explode`, serialize request bodies by content type, and decode responses by media type. Generated operations carry the metadata. Most requests need no additional configuration.
## Override parameter serialization
Check the [default parameter styles and encoding](/docs/5.x/reference/serialization#parameter-styles) before replacing a serializer.
Override `serializer.query`, `.body`, or `.path` on the client. Omitted functions retain their defaults:
```typescript
import { client } from './gen/.kubb/client'
import qs from 'qs'
client.setConfig({
serializer: {
query: (params) => qs.stringify(params, { arrayFormat: 'brackets' }),
},
})
```
A serializer set this way runs for every call, but you can pass `serializer` on a single call to
override just that request.
## Encode request bodies
The request content type decides how the body is encoded. The default serializer handles the
common types: a plain object becomes JSON, `multipart/form-data` becomes `FormData`, and
`application/x-www-form-urlencoded` becomes `URLSearchParams`. Binary and already-encoded bodies
(`FormData`, `URLSearchParams`, `Blob`, `ArrayBuffer`, typed arrays, and strings) pass through
untouched.
When an operation declares a single request content type, Kubb sets it on the generated function,
so you pass only the body. For an operation that accepts more than one, set
[`contentType`](/plugins/plugin-fetch/guide/calling-operations#set-the-content-type) on the
call.
> [!NOTE]
> When the body is `FormData`, the runtime removes any `Content-Type` header so the transport sets
> it with the multipart boundary. You do not need to set `multipart/form-data` yourself, and a
> value you set is dropped for that request.
To encode a content type the default serializer does not handle, register a codec for that media
type. `codecs` is keyed by content type, and each entry holds a `serialize` for the request body
and a `deserialize` for the response, so either half is optional:
```typescript
import { client } from './gen/.kubb/client'
import { stringify } from 'yaml'
client.setConfig({
codecs: {
'application/yaml': { serialize: (body) => stringify(body) },
},
})
```
## Decode responses
The runtime reads the response `Content-Type` and decodes the body by it: JSON is parsed, text
stays a string, and a binary type becomes a `Blob`. The negotiated media type is on the result as
`contentType`, so a `switch (result.contentType)` narrows `data` for an operation that returns
more than one.
To decode a media type the runtime does not handle, register a codec's `deserialize` for it. It
receives the raw body and the content type and returns the parsed value, and runs before
validation, so a custom format is transformed first and then checked against its schema:
```typescript
import { client } from './gen/.kubb/client'
client.setConfig({
codecs: {
'application/xml': { deserialize: (raw) => new DOMParser().parseFromString(raw as string, 'application/xml') },
},
})
```
Like the other config, `codecs` can also be passed on a single call, and a per-call entry merges
over the client one for that content type.
When the runtime picks the wrong parse mode because a response omits its `Content-Type` or sets a
misleading one, force the mode with `responseType` on the call. It accepts `'json'`, `'text'`,
`'blob'`, `'arraybuffer'`, and `'document'` (`@kubb/plugin-axios` adds `'formdata'`):
```typescript
const { data } = await downloadInvoice({ path: { id: '123' }, responseType: 'blob' })
// data is a Blob even when the server leaves Content-Type unset
```
For `responseType: 'stream'`, see [server-sent events](/plugins/plugin-fetch/guide/server-sent-events).
For bidirectional formats, supply both `serialize` and `deserialize` in the media type codec, then set the operation’s request and response `contentType`.
## Validate responses
Enable `validator` on the client plugin and register `pluginZod` in the same configuration. Validation is disabled by default.
```typescript twoslash
import { pluginFetch } from '@kubb/plugin-fetch'
pluginFetch({ validator: 'zod' })
```
`'zod'` validates the success response body, and the error body when a non-2xx call does not
throw. Use the object form to opt in per direction, where `request` validates the request
body before the call goes out:
```typescript
pluginFetch({ validator: { request: 'zod', response: 'zod' } })
```
With a validator set, Kubb passes the matching schema to each generated call, and the runtime
parses the body through it. The schemas are Standard Schema compatible, so this works the same
with Zod, valibot, and arktype. A body that does not match throws a `ParseError` carrying the
schema's `issues`, covered in
[error handling](/plugins/plugin-fetch/guide/error-handling#validation-failures).
## See also
- [HTTP serialization reference](/docs/5.x/reference/serialization)
- [Call operations](/plugins/plugin-fetch/guide/calling-operations)
- [Error handling](/plugins/plugin-fetch/guide/error-handling)
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
- [`@kubb/plugin-zod`](/plugins/plugin-zod/)
---
# Server-sent events
Source: https://kubb.dev/plugins/plugin-fetch/guide/server-sent-events
---
layout: doc
title: Server-sent events
description: Consume a text/event-stream operation from the client Kubb generates. Iterate typed Server-Sent Events with for-await, read the native response, and stop the stream with an AbortSignal.
outline: deep
---
# Server-sent events
Operations returning `text/event-stream` produce a typed stream rather than a `RequestResult`. Both clients support streaming. Axios uses its fetch adapter unless you select another adapter.
## Consume a stream
Read the `EventStreamResult.stream` with `for await`:
```typescript
import { streamEvents } from './gen/clients/streamEvents'
const { stream } = await streamEvents({})
for await (const event of stream) {
console.info(event.data)
}
```
`data` is typed from the operation's response schema. Each event also carries the optional SSE
fields, so you can branch on the event name or read the id:
```typescript
type ServerSentEvent = {
data: TData
event?: string
id?: string
retry?: number
}
```
The runtime parses each event's `data` as JSON when it is valid and keeps it as the raw string
otherwise.
## Stop early
Break out of the loop to stop reading. The underlying reader is canceled when the iterator
finishes, so an early `break` releases the stream:
```typescript
const { stream } = await streamEvents({})
for await (const event of stream) {
if (event.event === 'done') break
render(event.data)
}
```
To stop a stream from outside the loop, pass an `AbortSignal` and abort it. Aborting ends the
request and the iteration:
```typescript
const controller = new AbortController()
const { stream } = await streamEvents({ signal: controller.signal })
// elsewhere, for example on component unmount
controller.abort()
```
## Read the response
Read status and headers from the native `response` before iterating:
```typescript
const { stream, response } = await streamEvents({})
console.info(response.status) // 200
console.info(response.headers.get('x-stream-id'))
for await (const event of stream) {
handle(event.data)
}
```
## Handle errors
Catch connection failures and non-2xx responses around the call and iteration:
```typescript
import { ResponseError } from './gen/.kubb/client'
try {
const { stream } = await streamEvents({})
for await (const event of stream) {
handle(event.data)
}
} catch (error) {
if (ResponseError.is(error)) {
console.error('stream rejected', error.status)
}
}
```
A drop mid-stream ends the `for await` loop. Track the last event's `id` if you need to reconnect
from where the stream stopped.
## See also
- [Call operations](/plugins/plugin-fetch/guide/calling-operations)
- [Error handling](/plugins/plugin-fetch/guide/error-handling)
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
---
# Use a custom transport
Source: https://kubb.dev/plugins/plugin-fetch/guide/transport
---
layout: doc
title: Use a custom transport
description: Replace the transport the generated client sends through. Wrap fetch with retries or pass a pre-configured axios instance, while Kubb keeps owning URLs, serialization, and auth.
outline: deep
---
# Use a custom transport
Set a transport at runtime to replace the network send. The client still builds URLs, serializes parameters, resolves authentication, and handles results.
Fetch accepts a transport function. Axios accepts an `AxiosInstance`. Use [interceptors](/plugins/plugin-fetch/guide/interceptors) for request headers or response logging.
## Fetch: a transport function
`@kubb/plugin-fetch` types the transport as a function that receives a fully resolved request and returns a result:
```typescript
type Transport = (request: ResolvedRequest) => Promise
type ResolvedRequest = {
url: string
method: string
headers: Record
body?: RequestBody
signal?: AbortSignal
credentials?: RequestCredentials
options?: FetchOptions
responseType?: ResponseType
}
type TransportResult = {
data: TData
status: number
statusText: string
headers: Headers
contentType?: string
request: Request
response: Response
}
```
The core hands you a `ResolvedRequest` with the URL built, the query and body serialized, and auth headers set. Return the parsed `data` along with the native `request` and `response`, so status, headers, and the raw body stay reachable on the result.
### Mock the network in tests
Return a fixed result from an isolated client in tests:
```typescript
import { createClient } from './gen/.kubb/client'
import { getPetById } from './gen/clients/getPetById'
const testClient = createClient({
transport: async (request) => ({
data: { id: 1, name: 'Fluffy' },
status: 200,
statusText: 'OK',
headers: new Headers(),
request: new Request(request.url),
response: new Response(),
}),
})
const { data } = await getPetById({ path: { petId: 1 }, client: testClient })
// ^ { id: 1, name: 'Fluffy' }
```
Pass the isolated client per operation or to a query plugin.
## Axios: a custom instance
`@kubb/plugin-axios` types the transport as an `AxiosInstance`. The default is `axios.create()`, and you replace it with your own pre-configured instance:
```typescript
type ClientConfig = {
// ...
transport?: AxiosInstance
}
```
Kubb forwards the resolved URL, query, body, and authentication to the instance.
### Pass a pre-configured instance
Give the client an instance with a timeout, default headers, and a logging interceptor:
```typescript
import axios from 'axios'
import { client } from './gen/.kubb/client'
const instance = axios.create({
timeout: 10_000,
headers: { 'X-Client': 'kubb' },
})
instance.interceptors.response.use((response) => {
console.info(`${response.config.method?.toUpperCase()} ${response.config.url} -> ${response.status}`)
return response
})
client.setConfig({ transport: instance })
```
Every generated function now sends through your instance, so its timeout, headers, and interceptors apply to each call. Any client interceptors registered through `client.interceptors` also transfer to the new transport automatically, preserving their IDs for `eject` and `update`.
> [!NOTE]
> Kubb sets `transformRequest`, `paramsSerializer`, and `validateStatus` on each request so its own serialization and `throwOnError` handling stay in charge. Configure cross-cutting concerns like timeouts, retries, and interceptors on the instance instead of overriding those fields. For a native axios field on a single call, such as `timeout` or `onUploadProgress`, pass [`options`](/plugins/plugin-axios/guide/calling-operations#pass-native-client-options) instead of building a new instance.
## Where to set it
Use `client.setConfig({ transport })` for shared configuration, `createClient({ transport })` for an isolated instance, or pass `transport` on an operation to override it for one call.
## See also
- [`@kubb/plugin-fetch`](/plugins/plugin-fetch/)
- [`@kubb/plugin-axios`](/plugins/plugin-axios/)
- [Interceptors](/plugins/plugin-fetch/guide/interceptors)
- [Authentication guide](/plugins/plugin-fetch/guide/authentication)
- [Set your own baseURL](/plugins/plugin-fetch/guide/base-url)
---
# Options
Source: https://kubb.dev/plugins/plugin-fetch/reference/options
---
layout: doc
title: Options
description: Configuration options for @kubb/plugin-fetch.
outline: deep
---
# Options
Pass these options to `pluginFetch()` to control what it generates and where the files go.
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| [`output`](#output) | `Output` | `{ path: 'clients', barrel: { type: 'named' } }` | Where the generated files are written and exported |
| [`group`](#group) | `Group` | — | Split output into per-tag or per-path folders |
| [`baseURL`](#baseurl) | `string` | — | Base URL prepended to every request |
| [`throwOnErrorDefault`](#throwonerrordefault) | `boolean` | `true` | Default error behavior and return type for generated operations |
| [`validator`](#validator) | `false \| 'zod' \| { request?: 'zod'; response?: 'zod' }` | `false` | Validate request and response bodies with Zod |
| [`comments`](#comments) | `'full' \| 'brief' \| 'none'` | `'full'` | How much of each description reaches the JSDoc |
| [`sdk`](#sdk) | `{ mode?: 'tag' \| 'flat'; name?: string }` | — | Generate a class-based SDK instead of functions |
| [`returnType`](#returntype) | `'full' \| 'data'` | `'full'` | Shape of the value a generated call resolves to |
| [`include`](#include) | `Array` | — | Keep only operations that match |
| [`exclude`](#exclude) | `Array` | `[]` | Skip operations that match |
| [`override`](#override) | `Array` | `[]` | Apply different options per pattern |
| [`resolver`](#resolver) | `ResolverPatch` | — | Customize generated names and file paths |
| [`macros`](#macros) | `Array` | — | Rewrite AST nodes before printing |
### output
Where the plugin writes its generated `.ts` files and how it exports them.
#### output.path
Folder for the plugin's files, resolved against the global `output.path` on `defineConfig` and defaulting to `'clients'`. To write everything to one file, set `output.mode: 'file'` and give `path` a file name with its extension, such as `'clients.ts'`.
#### output.mode
How the plugin consolidates its code into files, either `'file'` or `'directory'`.
- `'file'` writes everything into a single file, so `output.path` must include the extension (see above).
- `'directory'` writes one file per operation under `output.path`.
Leave it unset and Kubb reads `output.path`: a name with an extension means one file, anything else a directory.
> [!IMPORTANT]
> `group` requires directory output. Kubb infers the mode from `output.path`. Set `mode: 'directory'` to override that inference. Combining `group` with `mode: 'file'` stops generation with `KUBB_INVALID_PLUGIN_OPTIONS`.
#### output.barrel
Toggle the export style and depth to see the generated barrels.
::barrel-tree
::
Controls how the generated `index.ts` (barrel) re-exports the output. Accepts `{ type: 'named' }` or `{ type: 'all' }`, optionally with `nested: true` (for example `{ type: 'named', nested: true }`) to write an `index.ts` in every subdirectory, or `false` to skip the barrel entirely. Kubb reads the plugin's own `output.barrel` first, falls back to `config.output.barrel` on `defineConfig`, and finally to `false`. Every generator plugin ships a default `output` that sets `barrel: { type: 'named' }`, but passing your own `output` replaces that object wholesale, so repeat `barrel` whenever you set `output` yourself.
#### output.banner
Text added to the top of every generated file, such as a license header or `@ts-nocheck` directive. Pass a string, or a function `(meta: BannerMeta) => string` that receives the document info (`title`, `description`, `version`, `baseURL`) and per-file context (`filePath`, `baseName`, `isBarrel`, `isAggregation`), so a directive can skip barrel files.
#### output.footer
Text added to the bottom of every generated file (`string` or `(meta: BannerMeta) => string`), like `banner` but for closing comments. Pair `banner: '/* eslint-disable */'` with `footer: '/* eslint-enable */'` to scope a lint disable to the generated file.
### group
Switch the mode to see where these operations land on disk.
::grouping-diagram
::
Splits generated files into subfolders by the operation's tag or URL path, each under `{output.path}/{groupName}/`. Without `group`, every file lands directly in `output.path`. It applies only to `output.mode: 'directory'`.
> [!IMPORTANT]
> Combining `group` with `output.mode: 'file'` stops the build with a `KUBB_INVALID_PLUGIN_OPTIONS` error.
#### group.type
Property used to assign each operation to a group (`'tag' | 'path'`), required whenever `group` is set. An operation with no tag goes in the `default` group.
- `'tag'` uses the operation's first tag.
- `'path'` uses the first URL segment, such as `pet` for `/pet/{petId}`.
#### group.name
Function `(context: { group: string }) => string` that turns a group key into a folder name. It defaults to the camelCased tag for a `'tag'` group or the first path segment for a `'path'` group, and a `group.name` you pass always wins.
### baseURL
Base URL prepended to every request. When omitted, no host is prepended and each request uses the operation's relative path from the spec. A value containing a `${...}` interpolation is emitted as a template literal in the generated client config, so `baseURL: '${process.env.API_URL}'` reads the environment variable at runtime.
### throwOnErrorDefault
Set `throwOnErrorDefault: false` to return documented error responses as values by default. This sets the fallback on each generated request and the default `ThrowOnError` type parameter on standalone functions and SDK methods. A call with `throwOnError: true` still throws for a non-2xx response and narrows its return type to successful responses.
```typescript
pluginFetch({ throwOnErrorDefault: false })
const result = await getPetById({ path: { petId: 1 } })
if (result.error) console.error(result.error)
```
This setting applies to the whole plugin and cannot be set in `override`. Generated operations use it even when the client config changes; pass `throwOnError` on a call to override it. Query hooks continue to set `throwOnError: true` explicitly.
### validator
Runtime validator applied to request and response bodies using schemas from `@kubb/plugin-zod`, defaulting to `false`.
- `false` does no validation and returns the response cast to the generated type.
- `'zod'` validates the success response body, and the error body when a non-2xx call does not throw.
- `{ request?: 'zod', response?: 'zod' }` opts in per direction, validating the request body before the call and the response body after.
Add `@kubb/plugin-zod` to the plugins list when either direction is `'zod'`. With validation on the generated function throws a `ParseError` when a body fails its schema.
Add the schema plugin alongside the client. The following configuration validates both directions:
```typescript [kubb.config.ts]
import { defineConfig } from 'kubb'
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' },
plugins: [
pluginTs(),
pluginZod(),
pluginFetch({ validator: { request: 'zod', response: 'zod' } }),
],
})
```
### comments
Controls generated JSDoc. `'full'` (default) keeps complete descriptions. `'brief'` keeps the first sentence and other tags. Descriptions over 150 characters without a sentence ending are cut at the last word before 120. `'none'` omits JSDoc but keeps the generated-by banner.
### sdk
Generates a class-based SDK instead of standalone functions, accepting `{ mode?: 'tag' | 'flat'; name?: string }`. Each tag client is an instance class whose constructor takes a client config and builds its own client, so every environment is a separate instance. Leave `sdk` unset to keep the per-operation functions that the query plugins consume.
`mode: 'tag'` (the default) emits one class per tag, such as `PetClient` and `StoreClient`. Set `sdk.name` alongside it to also emit a composed root class that instantiates every tag client from one shared config, reached as `new PetStore(config).pet.getPetById(...)`. `mode: 'flat'` emits a single class named by `sdk.name` with every operation as a direct method.
`mode: 'tag'` needs one file per tag, so pairing it with a single-file `output` (`output.mode: 'file'`, or an `output.path` that already names a file such as `'clients.ts'`) throws [`KUBB_INVALID_PLUGIN_OPTIONS`](/docs/5.x/reference/diagnostics#kubb-invalid-plugin-options). Use `mode: 'flat'` for a single-file SDK, or give `output.path` a directory so `mode: 'tag'` can split per tag.
```typescript [kubb.config.ts]
import { pluginFetch } from '@kubb/plugin-fetch'
pluginFetch({ sdk: { mode: 'tag' } })
```
Construct a class with a client config, then call a method with the grouped options object (`{ path, query, headers, body }`). Each call resolves to `{ status, data, error, contentType, request, response }`. With the default `throwOnErrorDefault: true` setting, a resolved call means the request succeeded and `data` is set. Pass `throwOnError: false` to get the discriminated union instead, keyed on the top-level `status`:
```typescript
import { PetClient } from './src/gen/clients/petClient'
const pet = new PetClient({ baseURL: 'https://petstore.swagger.io/v2' })
const { status, data, error } = await pet.getPetById({ path: { petId: 1 }, throwOnError: false })
if (status === 200) {
console.log(data) // data is the success body, error is undefined
} else {
console.error(status, error) // status is the documented error code, error is its parsed body
}
```
### returnType
Shape of the value a generated call resolves to. `'full'` (the default) keeps `{ status, data, error, contentType, request, response }`. `'data'` unwraps that down to the bare success body when `throwOnError` is `true`, and falls back to the full result when `throwOnError` is `false`, since that result still needs `error` to tell success from failure.
```typescript
pluginFetch({ returnType: 'data' })
```
```typescript
const pet = await getPetById({ path: { petId: 1 } }) // Pet, not { status, data, ... }
```
This applies to the standalone functions and the class-based SDK. `@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, and `@kubb/plugin-mcp` read the same option, so their hooks and tool handlers give you the success body as `data` either way.
To read response headers such as `ETag` under `'data'`, pass `throwOnError: false` on the call. It then resolves to the full result, and a non-2xx comes back on `error` instead of throwing:
```typescript
const result = await getPetById({ path: { petId: 1 }, throwOnError: false })
if (result.error === undefined) {
const etag = result.response.headers.get('etag')
}
```
Dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, and `@kubb/plugin-mcp`) also honor per-operation `returnType` set through [`override`](#override), so their generated hooks and handlers match the shape of the resolved ``.
### include
Generates only the operations and schemas that match at least one entry, and skips the rest. Each entry filters by `tag`, `operationId`, `path`, `method`, `contentType`, or `schemaName`, with a `pattern` that can be a string or a `RegExp`, both matched as a regular expression against the value. A string pattern is compiled with `new RegExp(pattern)`, so it is not an exact match: `pattern: 'pet'` also matches `'petType'` or `'superpet'`.
```typescript [Type definition]
export type Include = {
type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
pattern: string | RegExp
}
```
### exclude
Skips any operation or schema that matches at least one entry, the opposite of `include`. Entries use the same `type` and `pattern` fields as `include`, and when both options match an item, `exclude` wins.
When operations are excluded on a client plugin (`@kubb/plugin-fetch` or `@kubb/plugin-axios`), dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, `@kubb/plugin-mcp`) skip generating hooks or handlers for those operations automatically, without requiring duplicate `exclude` configurations.
### override
Applies different plugin options to operations that match a pattern. Each entry takes the same `type` and `pattern` as `include`, plus an `options` object that accepts any plugin option except `override`, so rules cannot nest. The first matching entry merges onto the plugin defaults, and later entries do not stack.
```typescript [Type definition]
export type Override = {
type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
pattern: string | RegExp
options: Omit, 'override'>
}
```
When options such as `returnType`, `output`, or `group` are overridden on a client plugin (`@kubb/plugin-fetch` or `@kubb/plugin-axios`), dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, `@kubb/plugin-mcp`) resolve and follow those per-operation options automatically.
### resolver
Overrides generated file and symbol names. Omitted members keep the plugin's resolver defaults. See [Override a resolver](/docs/5.x/how-to/resolvers) for the `this` context and how a patch layers over the default.
> [!TIP]
> Inside a method `this` is the full resolver, so `this.default.name(name)` reuses the built-in casing.
```typescript [Partial override]
type ResolverClientPatch = {
name?(name: string): string
file?: {
baseName?(params: { name: string; extname: string }): string
path?(params: { baseName: string; output: Output }): string
}
className?(name: string): string
groupName?(name: string): string // → 'PetClient'
propertyName?(name: string): string
}
```
### macros
Rewrites AST nodes before they are printed, without forking the generator. Each [macro](/docs/5.x/how-to/macros) callback (such as `schema` or `operation`) receives the node and a context object, and returns a replacement or `undefined` to leave it as is. Omitted callbacks keep their defaults, and macros run in order, so a later one sees the output of an earlier one.
---
# Kubb MCP Plugin
Source: https://kubb.dev/plugins/plugin-mcp
---
layout: doc
title: Kubb MCP Plugin
description: Generates a Model Context Protocol server from your OpenAPI spec,
so AI assistants can call each operation as a typed tool.
outline: deep
guides:
- id: server
title: Run an MCP server
kind: plugin
id: plugin-mcp
name: MCP
category: ai
type: official
npmPackage: "@kubb/plugin-mcp"
repo: https://github.com/kubb-labs/plugins
docsPath: /plugins/plugin-mcp
featured: false
icon:
light: https://kubb.dev/feature/mcp-light.svg
dark: https://kubb.dev/feature/mcp-dark.svg
maintainers:
- name: Stijn Van Hulle
github: stijnvanhulle
compatibility:
kubb: ">=5.0.0"
node: ">=22"
tags:
- mcp
- model-context-protocol
- ai
- claude
- llm
- codegen
- openapi
dependencies:
- plugin-ts
- plugin-zod
resources:
documentation: https://kubb.dev/plugins/plugin-mcp
repository: https://github.com/kubb-labs/plugins
issues: https://github.com/kubb-labs/plugins/issues
changelog: https://github.com/kubb-labs/plugins/blob/main/packages/plugin-mcp/CHANGELOG.md
codesandbox: https://codesandbox.io/p/github/kubb-labs/plugins/main/examples/mcp
---
# @kubb/plugin-mcp
`@kubb/plugin-mcp` generates MCP tools, handlers, and a server from OpenAPI operations. Each tool calls a generated HTTP client and uses Zod schemas for validation.
## Installation
::code-group{sync="package-manager"}
```shell [bun]
bun add -d @kubb/plugin-mcp
```
```shell [pnpm]
pnpm add -D @kubb/plugin-mcp
```
```shell [npm]
npm install --save-dev @kubb/plugin-mcp
```
```shell [yarn]
yarn add -D @kubb/plugin-mcp
```
::
## Dependencies
Add [`pluginTs`](/plugins/plugin-ts/), [`pluginZod`](/plugins/plugin-zod/), and an [Axios](/plugins/plugin-axios/) or [Fetch](/plugins/plugin-fetch/) client plugin. Select [`client`](./reference/options#client) when both clients are configured. Generated servers require `@modelcontextprotocol/sdk` v1 or higher.
## Example
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginZod } from '@kubb/plugin-zod'
import { pluginFetch } from '@kubb/plugin-fetch'
import { pluginMcp } from '@kubb/plugin-mcp'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
plugins: [
pluginTs(),
pluginZod(),
pluginFetch({ baseURL: 'https://petstore.swagger.io/v2' }),
pluginMcp({ output: { path: 'mcp', mode: 'directory' } }),
],
})
```
## Documentation
- [Options](./reference/options)
- [Run an MCP server](./guide/server)
## See also
- [Connect Claude to a remote MCP server](https://modelcontextprotocol.io/docs/tools/claude-desktop)
- [Changelog](https://github.com/kubb-labs/plugins/blob/main/packages/plugin-mcp/CHANGELOG.md)
---
# Run an MCP server
Source: https://kubb.dev/plugins/plugin-mcp/guide/server
---
layout: doc
title: Run an MCP server
description: Generate and start an MCP server with typed tools and an HTTP client.
outline: deep
---
# Run an MCP server
## Generate the server
Register TypeScript types, Zod schemas, and an Axios or Fetch client alongside `pluginMcp`. A single registered client is detected automatically.
```typescript [kubb.config.ts]
import { defineConfig } from 'kubb'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginZod } from '@kubb/plugin-zod'
import { pluginAxios } from '@kubb/plugin-axios'
import { pluginMcp } from '@kubb/plugin-mcp'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
plugins: [
pluginTs({ output: { path: 'types', mode: 'directory' } }),
pluginZod(),
pluginAxios({ baseURL: 'https://petstore.swagger.io/v2' }),
pluginMcp({ output: { path: 'mcp', mode: 'directory' } }),
],
})
```
Run `kubb generate`. The plugin writes tool handlers, `server.ts`, and `.mcp.json`. Each generated handler calls its HTTP operation, forwards the cancellation signal, and throws for non-2xx responses. Tool results include text content and structured response data. `server.ts` registers tools with Zod input and output schemas.
## Start the server
Install the generated server's runtime dependencies and a TypeScript runner:
```shell [Terminal]
npm install @modelcontextprotocol/sdk@^1 zod@^4 axios@^1
npm install -D tsx
```
Import the generated `startServer` function.
```typescript [server.ts]
import { startServer } from './src/gen/mcp/server'
await startServer()
```
Run the entry point:
```shell [Terminal]
npx tsx server.ts
```
## Select a client
When both HTTP plugins are registered, set [`client`](/plugins/plugin-mcp/reference/options#client) explicitly. Give each client a different output directory so their generated files do not overwrite each other.
```typescript [kubb.config.ts]
import { pluginAxios } from '@kubb/plugin-axios'
import { pluginFetch } from '@kubb/plugin-fetch'
import { pluginMcp } from '@kubb/plugin-mcp'
const clients = [
pluginAxios({ output: { path: 'clients-axios', mode: 'directory' } }),
pluginFetch({ output: { path: 'clients-fetch', mode: 'directory' } }),
pluginMcp({ output: { path: 'mcp', mode: 'directory' }, client: 'axios' }),
]
```
Add these entries alongside `pluginTs` and `pluginZod` in `defineConfig.plugins`. The handlers import operations from `clients-axios`.
---
# Options
Source: https://kubb.dev/plugins/plugin-mcp/reference/options
---
layout: doc
title: Options
description: Configuration options for @kubb/plugin-mcp.
outline: deep
---
# Options
Options for `@kubb/plugin-mcp`, which generates an MCP server where each OpenAPI operation becomes a typed tool handler.
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| [`output`](#output) | `Output` | `{ path: 'mcp', barrel: { type: 'named' } }` | Where the generated handlers are written and exported |
| [`group`](#group) | `Group` | — | Split output into per-tag or per-path folders |
| [`client`](#client) | `'fetch' \| 'axios'` | — | Which registered client plugin the handlers call |
| [`include`](#include) | `Array` | — | Keep only operations that match |
| [`exclude`](#exclude) | `Array` | `[]` | Skip operations that match |
| [`override`](#override) | `Array` | `[]` | Apply different options per pattern |
| [`resolver`](#resolver) | `ResolverPatch` | — | Customize generated names and file paths |
| [`macros`](#macros) | `Array` | — | Rewrite AST nodes before printing |
### output
Where the generated MCP handler files are written and how they are exported.
#### output.path
Folder where the plugin writes its files, resolved against the global `output.path` on `defineConfig` and defaulting to `mcp`. Give it an extensionless directory name — `path` naming a single file (with `output.mode: 'file'`, explicit or inferred from an extension) is rejected, since `plugin-mcp` always writes `server.ts` and `.mcp.json` alongside the per-operation handlers.
#### output.mode
How the plugin consolidates its generated code. Only `'directory'` is supported: one file per operation is written under `output.path`, next to the fixed `server.ts` and `.mcp.json` files. `output.mode: 'file'` fails the build with a setup error, since a single file has nowhere to put those extra files — leave `mode` unset (an extensionless `output.path`, the default `mcp`, resolves to `'directory'`) or set it to `'directory'` explicitly.
#### output.barrel
Toggle the export style and depth to see the generated barrels.
::barrel-tree
::
Controls how the generated `index.ts` (barrel) re-exports the output. Accepts `{ type: 'named' }` or `{ type: 'all' }`, optionally with `nested: true` (for example `{ type: 'named', nested: true }`) to write an `index.ts` in every subdirectory, or `false` to skip the barrel entirely. Kubb reads the plugin's own `output.barrel` first, falls back to `config.output.barrel` on `defineConfig`, and finally to `false`. Every generator plugin ships a default `output` that sets `barrel: { type: 'named' }`, but passing your own `output` replaces that object wholesale, so repeat `barrel` whenever you set `output` yourself.
#### output.banner
Text added to the top of every generated file, such as a license header or `@ts-nocheck` directive. Pass a string, or a function `(meta: BannerMeta) => string` that receives the document info (`title`, `description`, `version`, `baseURL`) and per-file context (`filePath`, `baseName`, `isBarrel`, `isAggregation`), so a directive can skip barrel files.
#### output.footer
Text added to the bottom of every generated file (`string` or `(meta: BannerMeta) => string`), like `banner` but for closing comments. Pair `banner: '/* eslint-disable */'` with `footer: '/* eslint-enable */'` to scope a lint disable to the generated file.
### group
Switch the mode to see where these operations land on disk.
::grouping-diagram
::
Splits generated files into subfolders by the operation's tag or URL path, each under `{output.path}/{groupName}/`. Without `group`, every file lands directly in `output.path`. It applies only to `output.mode: 'directory'`.
> [!IMPORTANT]
> Combining `group` with `output.mode: 'file'` stops the build with a `KUBB_INVALID_PLUGIN_OPTIONS` error.
#### group.type
Property used to assign each operation to a group (`'tag' | 'path'`), required whenever `group` is set. An operation with no tag goes in the `default` group.
- `'tag'` uses the operation's first tag.
- `'path'` uses the first URL segment, such as `pet` for `/pet/{petId}`.
#### group.name
Function that turns a group key into the subdirectory name under `output.path`. It defaults to the camelCased tag for `tag` groups and the first URL segment as-is for `path` groups. The `server.ts` and `.mcp.json` files keep their fixed names at the root of `output.path`.
### client
Selects which registered client plugin the handlers call, `'fetch'` for `@kubb/plugin-fetch` or `'axios'` for `@kubb/plugin-axios`. Each handler calls that client's generated `` for the operation, passing one grouped `{ path, query, headers, body }` object. A lone registered client plugin is auto-detected, so set this only to disambiguate when both are registered, and transport options such as `baseURL` live on the client plugin itself.
Generated handlers follow the client plugin's `returnType` option:
- Under `returnType: 'full'` (default), handlers read the success body from `res.data`.
- Under `returnType: 'data'`, handlers use the resolved call result directly as the tool response body.
- Handlers always pass `throwOnError: true` so non-2xx responses throw and surface as tool errors, regardless of client-level `throwOnErrorDefault` settings.
> [!NOTE]
> The handlers call a client plugin's functions, so register `@kubb/plugin-fetch` or `@kubb/plugin-axios` alongside this one.
### include
Generates only the operations and schemas that match at least one entry, and skips the rest. Each entry filters by `tag`, `operationId`, `path`, `method`, `contentType`, or `schemaName`, with a `pattern` that can be a string or a `RegExp`, both matched as a regular expression against the value. A string pattern is compiled with `new RegExp(pattern)`, so it is not an exact match: `pattern: 'pet'` also matches `'petType'` or `'superpet'`.
```typescript [Type definition]
export type Include = {
type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
pattern: string | RegExp
}
```
### exclude
Skips any operation or schema that matches at least one entry, the opposite of `include`. Entries use the same `type` and `pattern` fields as `include`, and when both options match an item, `exclude` wins.
When operations are excluded on a client plugin (`@kubb/plugin-fetch` or `@kubb/plugin-axios`), dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, `@kubb/plugin-mcp`) skip generating hooks or handlers for those operations automatically, without requiring duplicate `exclude` configurations.
### override
Applies different plugin options to operations that match a pattern. Each entry takes the same `type` and `pattern` as `include`, plus an `options` object that accepts any plugin option except `override`, so rules cannot nest. The first matching entry merges onto the plugin defaults, and later entries do not stack.
```typescript [Type definition]
export type Override = {
type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
pattern: string | RegExp
options: Omit, 'override'>
}
```
When options such as `returnType`, `output`, or `group` are overridden on a client plugin (`@kubb/plugin-fetch` or `@kubb/plugin-axios`), dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, `@kubb/plugin-mcp`) resolve and follow those per-operation options automatically.
### resolver
A partial patch changes how the plugin names generated files and symbols. Override only the members you want, and anything you omit keeps `resolverMcp`. See [Override a resolver](/docs/5.x/how-to/resolvers) for the `this` context and how a patch layers over the default.
> [!TIP]
> Inside a method `this` is the full resolver, so `this.default.name(name)` reuses the built-in casing.
```typescript [Partial override]
type ResolverMcpPatch = {
name?(name: string): string
file?: {
baseName?(params: { name: string; extname: string }): string
path?(params: { baseName: string; output: Output }): string
}
handler?: {
name?(node: OperationNode): string // → 'showPetByIdHandler'
}
}
```
### macros
Rewrites AST nodes before they are printed, without forking the generator. Each [macro](/docs/5.x/how-to/macros) callback (such as `schema` or `operation`) receives the node and a context object, and returns a replacement or `undefined` to leave it as is. Omitted callbacks keep their defaults, and macros run in order, so a later one sees the output of an earlier one.
---
# Kubb MSW Plugin
Source: https://kubb.dev/plugins/plugin-msw
---
layout: doc
title: Kubb MSW Plugin
description: Generates MSW request handlers from your OpenAPI spec so you can
mock the API in tests and during local development.
outline: deep
guides:
- id: mock-api
title: Mock API responses
kind: plugin
id: plugin-msw
name: MSW
category: mocks
type: official
npmPackage: "@kubb/plugin-msw"
repo: https://github.com/kubb-labs/plugins
docsPath: /plugins/plugin-msw
featured: false
icon:
light: https://kubb.dev/feature/msw.svg
maintainers:
- name: Stijn Van Hulle
github: stijnvanhulle
compatibility:
kubb: ">=5.0.0"
node: ">=22"
tags:
- msw
- mock-service-worker
- api-mocking
- mocks
- testing
- codegen
- openapi
dependencies:
- plugin-ts
- plugin-faker
resources:
documentation: https://kubb.dev/plugins/plugin-msw
repository: https://github.com/kubb-labs/plugins
issues: https://github.com/kubb-labs/plugins/issues
changelog: https://github.com/kubb-labs/plugins/blob/main/packages/plugin-msw/CHANGELOG.md
codesandbox: https://codesandbox.io/p/github/kubb-labs/plugins/main/examples/msw
---
# @kubb/plugin-msw
`@kubb/plugin-msw` generates typed [MSW](https://mswjs.io/) handlers from OpenAPI operations. Supply response data in tests or generate it with Faker.
## Installation
::code-group{sync="package-manager"}
```shell [bun]
bun add -d @kubb/plugin-msw
```
```shell [pnpm]
pnpm add -D @kubb/plugin-msw
```
```shell [npm]
npm install --save-dev @kubb/plugin-msw
```
```shell [yarn]
yarn add -D @kubb/plugin-msw
```
::
## Dependencies
Add [`pluginTs`](/plugins/plugin-ts/). Add [`pluginFaker`](/plugins/plugin-faker/) when `parser: 'faker'`. The default `parser: 'data'` does not need it. Generated handlers require MSW v2 or higher.
## Example
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginMsw } from '@kubb/plugin-msw'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
plugins: [
pluginTs(),
pluginMsw({ output: { path: 'handlers', mode: 'directory' }, handlers: true }),
],
})
```
## Documentation
- [Options](./reference/options)
- [Mock API responses](./guide/mock-api)
## See also
- [MSW](https://mswjs.io/)
- [@kubb/plugin-ts](/plugins/plugin-ts/)
- [@kubb/plugin-faker](/plugins/plugin-faker/)
- [Changelog](https://github.com/kubb-labs/plugins/blob/main/packages/plugin-msw/CHANGELOG.md)
---
# Mock API responses
Source: https://kubb.dev/plugins/plugin-msw/guide/mock-api
---
layout: doc
title: Mock API responses
description: Configure generated MSW handlers and register them in tests or a
browser worker.
outline: deep
---
# Mock API responses
## Generate typed handlers
Add `pluginTs` and `pluginMsw` to your configuration. The default `parser: 'data'` creates handlers whose response data you supply.
```typescript [kubb.config.ts]
import { defineConfig } from 'kubb'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginMsw } from '@kubb/plugin-msw'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
plugins: [
pluginTs(),
pluginMsw({ output: { path: 'handlers', mode: 'directory' }, baseURL: 'http://localhost:3000', handlers: true }),
],
})
```
For Node tests, set `baseURL` to the URL your client calls so MSW can match absolute requests. Browser handlers can use relative paths for the current origin.
## Supply test data
Call a generated handler with a response body or a callback that returns a `Response`.
```typescript [test-server.ts]
import { setupServer } from 'msw/node'
import { getPetByIdHandler } from './src/gen/handlers/getPetByIdHandler'
export const server = setupServer(
getPetByIdHandler({ name: 'Rex', photoUrls: [] }),
)
```
## Generate mock data
Add `pluginFaker` and change the MSW [`parser`](/plugins/plugin-msw/reference/options#parser) to `'faker'`.
```typescript [kubb.config.ts]
import { pluginTs } from '@kubb/plugin-ts'
import { pluginFaker } from '@kubb/plugin-faker'
import { pluginMsw } from '@kubb/plugin-msw'
const plugins = [
pluginTs(),
pluginFaker(),
pluginMsw({
output: { path: 'handlers', mode: 'directory' },
baseURL: 'http://localhost:3000',
parser: 'faker',
handlers: true,
}),
]
```
Use this array as `defineConfig.plugins`. Calling `getPetByIdHandler()` without data now uses generated Faker values.
## Register the handler collection
With `handlers: true`, Kubb exports an operation-ordered collection from `handlers.ts`. Register it in Node or a browser.
::code-group
```typescript [test-server.ts]
import { setupServer } from 'msw/node'
import { handlers } from './src/gen/handlers/handlers'
export const server = setupServer(...handlers)
```
```typescript [browser-worker.ts]
import { setupWorker } from 'msw/browser'
import { handlers } from './src/gen/handlers/handlers'
export const worker = setupWorker(...handlers)
```
::
Start the server or worker through your test or application setup.
---
# Options
Source: https://kubb.dev/plugins/plugin-msw/reference/options
---
layout: doc
title: Options
description: Configuration options for @kubb/plugin-msw.
outline: deep
---
# Options
Every option below is a key on `pluginMsw({ ... })`.
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| [`output`](#output) | `Output` | `{ path: 'handlers', barrel: { type: 'named' } }` | Where the generated files are written and exported |
| [`group`](#group) | `Group` | — | Split output into per-tag or per-path folders |
| [`baseURL`](#baseurl) | `string` | — | Base URL prepended to every handler's request |
| [`handlers`](#handlers) | `boolean` | `false` | Emit a `handlers.ts` that re-exports every handler |
| [`parser`](#parser) | `'data' \| 'faker'` | `'data'` | Source of the response body each handler returns |
| [`include`](#include) | `Array` | — | Keep only operations that match |
| [`exclude`](#exclude) | `Array` | `[]` | Skip operations that match |
| [`override`](#override) | `Array` | `[]` | Apply different options per pattern |
| [`resolver`](#resolver) | `ResolverPatch` | — | Customize generated names and file paths |
| [`macros`](#macros) | `Array` | — | Rewrite AST nodes before printing |
### output
Where the generated handler files are written and how they are exported.
#### output.path
Folder where the plugin writes its files, resolved against the global `output.path` on `defineConfig` and defaulting to `'handlers'`. To write everything to one file instead, set `output.mode: 'file'` and give `path` a file name with its extension, such as `'handlers.ts'`.
#### output.mode
How the plugin consolidates its generated code into files. `'file'` writes everything into a single file, so `output.path` must include the extension. `'directory'` writes one file per operation under `output.path`. Pair `'directory'` with `group` to organize output into subdirectories. Leave it unset and Kubb reads `output.path`: a name with an extension means one file, anything else a directory.
> [!IMPORTANT]
> `group` requires directory output. Kubb infers the mode from `output.path`. Set `mode: 'directory'` to override that inference. Combining `group` with `mode: 'file'` stops generation with `KUBB_INVALID_PLUGIN_OPTIONS`.
#### output.barrel
Toggle the export style and depth to see the generated barrels.
::barrel-tree
::
Controls how the generated `index.ts` (barrel) re-exports the output. Accepts `{ type: 'named' }` or `{ type: 'all' }`, optionally with `nested: true` (for example `{ type: 'named', nested: true }`) to write an `index.ts` in every subdirectory, or `false` to skip the barrel entirely. Kubb reads the plugin's own `output.barrel` first, falls back to `config.output.barrel` on `defineConfig`, and finally to `false`. Every generator plugin ships a default `output` that sets `barrel: { type: 'named' }`, but passing your own `output` replaces that object wholesale, so repeat `barrel` whenever you set `output` yourself.
#### output.banner
Text added to the top of every generated file, such as a license header or `@ts-nocheck` directive. Pass a string, or a function `(meta: BannerMeta) => string` that receives the document info (`title`, `description`, `version`, `baseURL`) and per-file context (`filePath`, `baseName`, `isBarrel`, `isAggregation`), so a directive can skip barrel files.
#### output.footer
Text added to the bottom of every generated file (`string` or `(meta: BannerMeta) => string`), like `banner` but for closing comments. Pair `banner: '/* eslint-disable */'` with `footer: '/* eslint-enable */'` to scope a lint disable to the generated file.
### group
Switch the mode to see where these operations land on disk.
::grouping-diagram
::
Splits generated files into subfolders by the operation's tag or URL path, each under `{output.path}/{groupName}/`. Without `group`, every file lands directly in `output.path`. It applies only to `output.mode: 'directory'`.
> [!IMPORTANT]
> Combining `group` with `output.mode: 'file'` stops the build with a `KUBB_INVALID_PLUGIN_OPTIONS` error.
#### group.type
Property used to assign each operation to a group (`'tag' | 'path'`), required whenever `group` is set. An operation with no tag goes in the `default` group.
- `'tag'` uses the operation's first tag.
- `'path'` uses the first URL segment, such as `pet` for `/pet/{petId}`.
#### group.name
Function that turns a group key into the subdirectory name. It defaults to `({ group }) => camelCase(group)` for tag groups, while `type: 'path'` groups default to the raw first URL segment, uncased.
### baseURL
Base URL prepended to every handler's request. When omitted, no host is prepended and each handler matches the operation's relative path from the spec. Set it to point at a different environment than the spec.
### handlers
Emits a `handlers.ts` file that re-exports every generated handler in operation order. Spread it into your MSW `setupServer(...handlers)` or `setupWorker(...handlers)` call.
```typescript [gen/handlers.ts]
import { getPetHandler } from './getPetHandler'
import { addPetHandler } from './addPetHandler'
export const handlers = [getPetHandler(), addPetHandler()] as const
```
### parser
Source of the response body each handler returns. `'data'` (the default) returns a typed payload from `@kubb/plugin-ts` that you fill in from tests, so the handler body is `new Response(JSON.stringify(data), ...)`. `'faker'` falls back to a value built by `@kubb/plugin-faker` when you pass no data. Register `pluginFaker()` in the plugins array, since the plugin depends on Faker only for this value.
```typescript ['faker']
export function getPetHandler(data?: GetPetQueryResponse | ((info: Parameters[1]>[0]) => Response | Promise)) {
return http.get('/pet/:petId', function handler(info) {
if (typeof data === 'function') return data(info)
return new Response(JSON.stringify(data || createGetPetQueryResponse(data)), {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
})
}
```
### include
Generates only the operations and schemas that match at least one entry, and skips the rest. Each entry filters by `tag`, `operationId`, `path`, `method`, `contentType`, or `schemaName`, with a `pattern` that can be a string or a `RegExp`, both matched as a regular expression against the value. A string pattern is compiled with `new RegExp(pattern)`, so it is not an exact match: `pattern: 'pet'` also matches `'petType'` or `'superpet'`.
```typescript [Type definition]
export type Include = {
type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
pattern: string | RegExp
}
```
### exclude
Skips any operation or schema that matches at least one entry, the opposite of `include`. Entries use the same `type` and `pattern` fields as `include`, and when both options match an item, `exclude` wins.
When operations are excluded on a client plugin (`@kubb/plugin-fetch` or `@kubb/plugin-axios`), dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, `@kubb/plugin-mcp`) skip generating hooks or handlers for those operations automatically, without requiring duplicate `exclude` configurations.
### override
Applies different plugin options to operations that match a pattern. Each entry takes the same `type` and `pattern` as `include`, plus an `options` object that accepts any plugin option except `override`, so rules cannot nest. The first matching entry merges onto the plugin defaults, and later entries do not stack.
```typescript [Type definition]
export type Override = {
type: 'tag' | 'operationId' | 'path' | 'method' | 'contentType' | 'schemaName'
pattern: string | RegExp
options: Omit, 'override'>
}
```
When options such as `returnType`, `output`, or `group` are overridden on a client plugin (`@kubb/plugin-fetch` or `@kubb/plugin-axios`), dependent plugins (`@kubb/plugin-react-query`, `@kubb/plugin-vue-query`, `@kubb/plugin-swr`, `@kubb/plugin-mcp`) resolve and follow those per-operation options automatically.
### resolver
Overrides generated file and symbol names. Omitted members keep the plugin's resolver defaults. See [Override a resolver](/docs/5.x/how-to/resolvers) for the `this` context and how a patch layers over the default.
> [!TIP]
> Inside a method `this` is the full resolver, so `this.default.name(name)` reuses the built-in casing.
```typescript [Partial override]
type ResolverMswPatch = {
name?(name: string): string
file?: {
baseName?(params: { name: string; extname: string }): string
path?(params: { baseName: string; output: Output }): string
}
handler?: {
name?(node: OperationNode): string
listName?(): string
}
}
```
### macros
Rewrites AST nodes before they are printed, without forking the generator. Each [macro](/docs/5.x/how-to/macros) callback (such as `schema` or `operation`) receives the node and a context object, and returns a replacement or `undefined` to leave it as is. Omitted callbacks keep their defaults, and macros run in order, so a later one sees the output of an earlier one.
---
# Kubb React Query Plugin
Source: https://kubb.dev/plugins/plugin-react-query
---
layout: doc
title: Kubb React Query Plugin
description: Generates typed TanStack Query hooks for React from your OpenAPI
spec, so reads and writes call your API through useQuery, useMutation, and
useInfiniteQuery without hand-written boilerplate.
outline: deep
guides:
- id: calling-operations
title: Call operations
kind: plugin
id: plugin-react-query
name: React Query
category: framework
type: official
npmPackage: "@kubb/plugin-react-query"
repo: https://github.com/kubb-labs/plugins
docsPath: /plugins/plugin-react-query
featured: true
icon:
light: https://kubb.dev/feature/tanstack.svg
maintainers:
- name: Stijn Van Hulle
github: stijnvanhulle
compatibility:
kubb: ">=5.0.0"
node: ">=22"
tags:
- react-query
- tanstack-query
- react
- hooks
- data-fetching
- codegen
- openapi
dependencies:
- plugin-ts
resources:
documentation: https://kubb.dev/plugins/plugin-react-query
repository: https://github.com/kubb-labs/plugins
issues: https://github.com/kubb-labs/plugins/issues
changelog: https://github.com/kubb-labs/plugins/blob/main/packages/plugin-react-query/CHANGELOG.md
codesandbox: https://codesandbox.io/p/github/kubb-labs/plugins/main/examples/react-query
---
# @kubb/plugin-react-query
`@kubb/plugin-react-query` generates TanStack Query option factories and cache keys from OpenAPI operations. Set `hooks: true` to also generate React hooks.
## Installation
::code-group{sync="package-manager"}
```shell [bun]
bun add -d @kubb/plugin-react-query
```
```shell [pnpm]
pnpm add -D @kubb/plugin-react-query
```
```shell [npm]
npm install --save-dev @kubb/plugin-react-query
```
```shell [yarn]
yarn add -D @kubb/plugin-react-query
```
::
## Dependencies
Add [`pluginTs`](/plugins/plugin-ts/) and an [Axios](/plugins/plugin-axios/) or [Fetch](/plugins/plugin-fetch/) client plugin. Generated output requires `@tanstack/react-query` v5 or higher. Configure validation on the client plugin.
## Example
```typescript twoslash [kubb.config.ts]
import { defineConfig } from 'kubb'
import { pluginTs } from '@kubb/plugin-ts'
import { pluginFetch } from '@kubb/plugin-fetch'
import { pluginReactQuery } from '@kubb/plugin-react-query'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
plugins: [
pluginTs(),
pluginFetch(),
pluginReactQuery({ hooks: true }),
],
})
```
## Documentation
- [Options](./reference/options)
- [Call operations](./guide/calling-operations)
## See also
- [TanStack Query](https://tanstack.com/query)
- [Changelog](https://github.com/kubb-labs/plugins/blob/main/packages/plugin-react-query/CHANGELOG.md)
---
# Call operations
Source: https://kubb.dev/plugins/plugin-react-query/guide/calling-operations
---
layout: doc
title: Call operations
description: Use the TanStack Query hooks Kubb generates from your OpenAPI spec.
Pass typed path, query, and header parameters and read data and error state
off the hook.
outline: deep
---
# Call operations
`@kubb/plugin-react-query` turns each operation into a hook that wraps the client function from `@kubb/plugin-axios` or `@kubb/plugin-fetch`. Read operations become `useFoo`, write operations become `useFoo` mutations, and every hook is typed from the spec.
> [!IMPORTANT]
> By default the plugin emits only the factory helpers (`queryOptions`, `mutationOptions`, `queryKey`, `mutationKey`). Set [`hooks: true`](/plugins/plugin-react-query/reference/options#hooks) in the plugin options to also generate the `use*` hooks shown below.
## Queries
A query hook takes the operation's grouped request config (`path`, `query`, `headers`, whichever the operation declares) as its first argument and returns a TanStack `UseQueryResult`:
```typescript
import { useGetPetById } from './gen/hooks/useGetPetById'
const { data, error, isLoading } = useGetPetById({ path: { petId: 1n } })
```
The second argument holds two option groups. `query` takes any TanStack Query option plus a `client` to target a specific `QueryClient`. `client` takes per-call request config for the underlying client, such as `baseURL` or `signal`:
```typescript
import { useGetPetById } from './gen/hooks/useGetPetById'
useGetPetById(
{ path: { petId: 1n } },
{
query: { staleTime: 60_000 },
client: { baseURL: 'https://api.example.com/v1' },
},
)
```
## Factories
Every operation also exports a `queryOptions` factory and a `queryKey` helper, so you can prefetch, seed, or compose outside a hook. The key includes the operation URL and path parameters, followed by query or body values when present:
```typescript
import { useQueryClient } from '@tanstack/react-query'
import { getPetByIdQueryKey, getPetByIdQueryOptions } from './gen/hooks/useGetPetById'
const queryClient = useQueryClient()
await queryClient.prefetchQuery(getPetByIdQueryOptions({ path: { petId: 1n } }))
queryClient.invalidateQueries({ queryKey: getPetByIdQueryKey({ path: { petId: 1n } }) })
```
## Mutations
A mutation hook takes only an options object. The grouped request config is the mutation variable, so you pass it to `mutate` or `mutateAsync`:
```typescript
import { useQueryClient } from '@tanstack/react-query'
import { useDeletePet } from './gen/hooks/useDeletePet'
const queryClient = useQueryClient()
const { mutate } = useDeletePet({
mutation: { onSuccess: () => queryClient.invalidateQueries() },
})
mutate({ path: { petId: 1n } })
```
A `mutationOptions` factory and `mutationKey` helper are exported next to the hook, mirroring the query factories.
## Errors and transport
The generated hooks call the client operation with `throwOnError: true`, so failures surface through the query library's `error` state, typed from the spec's error responses. Transport concerns (base URL, authentication, interceptors, serialization, validation) live on the client plugin: see the [plugin-fetch](/plugins/plugin-fetch/guide/calling-operations) or [plugin-axios](/plugins/plugin-axios/guide/calling-operations) guides.
## Customize cache keys
Set [`queryKey`](/plugins/plugin-react-query/reference/options#querykey) to change generated keys. String entries are emitted as source code, so use `JSON.stringify` for a string literal.
```typescript [kubb.config.ts]
import { pluginReactQuery } from '@kubb/plugin-react-query'
pluginReactQuery({
queryKey: ({ node }) => [JSON.stringify(node.operationId)],
})
```
This produces a fixed key such as `['getUserByName']`, independent of arguments. Include relevant path and query parameters in the key when their values identify different resources.
## Load more pages
Configure [`infinite`](/plugins/plugin-react-query/reference/options#infinite) with a query parameter declared by the operation, its initial value, and the response path for the next cursor.
```typescript [kubb.config.ts]
import { pluginReactQuery } from '@kubb/plugin-react-query'
pluginReactQuery({
hooks: true,
infinite: {
queryParam: 'page',
initialPageParam: 0,
nextParam: 'pagination.next.cursor',
},
})
```
Only operations with a `page` query parameter receive infinite-query output. Change `nextParam` to match your API's response.
Use the generated factory with TanStack Query:
```typescript [usage.ts]
import { useInfiniteQuery } from '@tanstack/react-query'
import { findPetsByTagsInfiniteQueryOptions } from './gen/hooks/useFindPetsByTagsInfinite'
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery(
findPetsByTagsInfiniteQueryOptions({ query: { tags: ['dog'] } }),
)
```
## Use suspense
Set `suspense: {}` and `hooks: true` to generate suspense hooks alongside regular hooks. Suspense requires TanStack Query v5 or higher.
```typescript [kubb.config.ts]
import { pluginReactQuery } from '@kubb/plugin-react-query'
pluginReactQuery({ hooks: true, suspense: {} })
```
```typescript [usage.ts]
import { useGetPetByIdSuspense } from './gen/hooks/useGetPetByIdSuspense'
const { data } = useGetPetByIdSuspense({ path: { petId: 1n } })
```
## Share hook options
Set [`customOptions`](/plugins/plugin-react-query/reference/options#customoptions) to call one options function from every generated hook.
```typescript [kubb.config.ts]
import { pluginReactQuery } from '@kubb/plugin-react-query'
pluginReactQuery({
hooks: true,
customOptions: {
importPath: './useCustomHookOptions',
name: 'useCustomHookOptions',
},
})
```
Place the function where the generated import resolves. Each hook passes `{ hookName, operationId }`. The generated barrel re-exports `HookOptions`.
```typescript [src/gen/hooks/useCustomHookOptions.ts]
import type { HookOptions } from './HookOptions'
export function useCustomHookOptions(
{ hookName }: { hookName: keyof HookOptions; operationId: string },
) {
if (hookName === 'useGetPetById') {
return { staleTime: 60_000 } satisfies HookOptions['useGetPetById']
}
return {}
}
```
Per-call query options override shared options.
---
# Options
Source: https://kubb.dev/plugins/plugin-react-query/reference/options
---
layout: doc
title: Options
description: Configuration options for @kubb/plugin-react-query.
outline: deep
---
# Options
Options for `pluginReactQuery`.
| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| [`output`](#output) | `Output` | `{ path: 'hooks', barrel: { type: 'named' } }` | Where the generated hooks are written and exported |
| [`group`](#group) | `Group` | — | Split output into per-tag or per-path folders |
| [`client`](#client) | `'axios' \| 'fetch'` | — | Which registered client plugin the hooks call |
| [`infinite`](#infinite) | `Partial \| false` | `false` | Generate `useInfiniteQuery` hooks for pagination |
| [`suspense`](#suspense) | `Partial