Create a Kubb plugin
Build a plugin that writes listPets.ts containing the method and path of one API operation, then test its output. You need Node.js 22.22 or higher, npm, and TypeScript knowledge.
Create a project
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 srcAll authoring APIs come from kubb/kit, included with kubb.
Add the specification
Create petStore.yaml in the project root:
openapi: 3.0.3
info:
title: Pets
version: 1.0.0
paths:
/pets:
get:
operationId: listPets
responses:
'200':
description: A list of petsCreate the plugin
defineExtension with kind: 'plugin' takes the plugin's meta and a setup function. Register a generator and resolver in setup with the setResolver and addGenerator composables, then return the plugin's options:
import { addGenerator, ast, createResolver, defineExtension, defineGenerator, setResolver } 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 = defineExtension({
kind: 'plugin',
meta: { name: 'plugin-example', version: '1.0.0' },
setup(options) {
setResolver(resolver)
addGenerator(operationsGenerator)
return options
},
})The operation handler writes a comment containing the operation's HTTP method and path.
Generate
Register the plugin in your config:
import { defineConfig } from 'kubb/config'
import { pluginExample } from './src/plugin'
export default defineConfig({
input: './petStore.yaml',
output: { path: './src/gen' },
plugins: [pluginExample()],
})npx kubb generateRead the generated file:
node -e "console.log(require('node:fs').readFileSync('src/gen/listPets.ts', 'utf8'))"It contains a comment naming GET and /pets.
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.
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:
npx vitest run src/plugin.test.tsVitest reports one passing test. It checks the generated file in memory without writing to disk.