Override a resolver
A resolver decides what generated code is called. Adapters turn a spec into the AST, generators walk the nodes, and a resolver names every symbol a plugin emits and picks the file each one lands in. @kubb/plugin-ts names its types, @kubb/plugin-zod names its schemas, and @kubb/plugin-react-query names its hooks and query keys, each through its own resolver.
Every code-generating plugin exposes that resolver through a resolver option, a partial patch over the plugin's built-in resolver. Set the top-level name to change identifier casing, file to rename or move the generated files, or a single namespaced method to rename one kind of symbol. Anything you leave out keeps the plugin default, so you change one naming rule without forking the plugin.
Shape
The patch mirrors the plugin's resolver, so you supply only the members you want to replace. name casts an identifier, file.baseName builds a file's base name, file.path owns its full path, and namespaces such as query, schema, and response group the per-operation names.
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 resolver, so write them as regular functions rather than arrow functions. this.default.name(name) runs the built-in casing your override replaced, so you can wrap it instead of rebuilding it. this.name(name) runs the plugin's own casing, which is what a namespaced method calls to stay in step with the top-level names.
Rename identifiers
name sets the casing for every symbol the plugin generates. This override prefixes each TypeScript type with Api and reuses the built-in PascalCase through this.default.name.
import { } from '@kubb/plugin-ts'
({
: {
() {
return `Api${this..()}`
},
},
})Rename and relocate files
file.baseName builds a file's name, extension included. Here it renames every Faker file to <name>.mock.ts instead of the plugin default.
import { } from '@kubb/plugin-faker'
({
: {
: {
({ , }) {
return `${}.mock${}`
},
},
},
})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.
import { } from '@kubb/plugin-faker'
({
: {
: {
({ , }) {
return `${.}/mocks/${}`
},
},
},
})Namespaced names
A plugin that emits more than one symbol per operation groups the extra names under namespaces. @kubb/plugin-react-query names its query keys through query.keyName. It shortens the default QueryKey suffix to Key here, and this.name keeps the operation casing consistent with the rest of the plugin.
import { } from '@kubb/plugin-react-query'
({
: {
: {
() {
return `${this.(.)}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.
import { } from '@kubb/plugin-ts'
({
: {
: {
(, ) {
return this.(`${.} ${} response`)
},
},
},
})Writing your own resolver
Plugin authors create complete resolvers with createResolver from kubb/kit. Pass at least { pluginName }, set name and file for the plugin's conventions, and add namespaces for the per-operation names. The built-in machinery stays reachable under this.default, so an override can fall back to it.
import { } from 'kubb/kit'
import type { , } from 'kubb/kit'
type = & {
: {
(: { : string }): string
}
}
type = <'plugin-docs', object, object, >
export const = <>({
: 'plugin-docs',
() {
return `${.(0).()}${.(1)}`
},
: {
() {
return this.(.)
},
},
})Call the resolver to get the names a generator would produce. The namespaced schema.name delegates to the top-level name, so both stay on the same casing.
resolverDocs.name('pet') // 'Pet'
resolverDocs.schema.name({ name: 'order' }) // 'Order'The resolver plugin option layers a user patch over this built-in resolver with Resolver.merge, which re-binds every helper and merges each namespace member by member. That is why overriding query.keyName keeps the default query.name, and why this.default still reaches the untouched built-ins inside an override.
See the Kit API reference for the helper and Resolver.merge, and Plugin concepts for how a plugin registers its resolver.