Skip to content

Middlewares

A middleware subscribes to lifecycle hooks and runs after every plugin handler for the same event. Plugins generate files, and middlewares observe and react to those files once they exist.

TIP

For barrel-file generation use the built-in @kubb/middleware-barrel. It is added by default when you import defineConfig from the top-level kubb package.

Quick start

Subscribe to kubb:plugins:end to inspect every generated file once all plugins have finished:

logMiddleware.ts
typescript
import {  } from '@kubb/core'

export const  = (() => ({
  : 'middleware-log',
  : {
    'kubb:plugins:end'({ ,  }) {
      .(`Generated ${.} files into ${..}`)
    },
  },
}))

Wire it into your config:

kubb.config.ts
typescript
import {  } from 'kubb'
import {  } from './logMiddleware.ts'
Cannot find module './logMiddleware.ts' or its corresponding type declarations.
export default ({ : { : './petStore.yaml' }, : { : './src/gen' }, : [()], })

Anatomy

Every middleware returned from defineMiddleware has the following shape:

Property Type Required Purpose
name string Yes Unique middleware identifier. Convention is middleware-<id>.
hooks { [K in keyof KubbHooks]?: (...args: KubbHooks[K]) => void | Promise<void> } Yes Map of KubbHooks handlers. Each handler runs after all plugin handlers for that event.

defineMiddleware takes a factory function and returns a callable. The factory runs once per build, so per-build state belongs in the closure:

stateful.ts
typescript
import {  } from '@kubb/core'

export const  = (() => {
  let  = 0

  return {
    : 'middleware-count',
    : {
      'kubb:plugin:end'() {
        ++
      },
      'kubb:plugins:end'({  }) {
        .(`${} plugins ran, ${.} files produced`)
      },
    },
  }
})

IMPORTANT

Middleware handlers always run last for any given event, after every plugin handler has completed. This guarantees that files is the full set when kubb:plugins:end fires.

Hooks

Hook Fires Context
kubb:plugin:end After each individual plugin { plugin, duration, success, error?, config, files, upsertFile }
kubb:plugins:end After all plugins have run { files, config, upsertFile }

Use kubb:plugin:end to track which plugins ran. Use kubb:plugins:end when you need the full file list, for barrel generation, manifests, audit logs, and notifications.

Call upsertFile(...files) inside kubb:plugins:end to add or merge files into the build output, the same way a generator does.

Naming convention

Middlewares follow the same layout as plugins and parsers:

Surface Pattern Example
npm package @<scope>/middleware-<name> or kubb-middleware-<name> @kubb/middleware-barrel
Middleware runtime name middleware-<name> (kebab-case, lowercase) 'middleware-barrel'
Factory export middleware<Name> (camelCase) middlewareBarrel
Name constant middleware<Name>Name middlewareBarrelName

Export the runtime name as a satisfies-typed constant so other code can reference it without typos:

naming.ts
typescript
import {  } from '@kubb/core'
import type {  } from '@kubb/core'

export const  = 'middleware-example' satisfies ['name']

export const  = (() => ({
  : ,
  : {
    'kubb:plugins:end'({  }) {
      .(`Total files: ${.}`)
    },
  },
}))

Built-in middlewares

@kubb/middleware-barrel

Generates index.ts barrel files for every plugin output directory and a root barrel at output.path/index.ts. Already enabled when you use defineConfig from kubb. See the @kubb/middleware-barrel reference for the full option list.

shell
bun add -d @kubb/middleware-barrel
shell
pnpm add -D @kubb/middleware-barrel
shell
npm install --save-dev @kubb/middleware-barrel
shell
yarn add -D @kubb/middleware-barrel
Export Purpose
middlewareBarrel Middleware factory that emits barrel files based on output.barrel.
middlewareBarrelName Stable string identifier ('middleware-barrel').

Configure it explicitly only when you need to customise barrel behaviour:

kubb.config.ts
typescript
import {  } from 'kubb'
import {  } from '@kubb/middleware-barrel'

export default ({
  : { : './petStore.yaml' },
  : { : './src/gen', : { : 'named' } },
  : [()],
})

NOTE

Valid barrel.type values are 'all' and 'named'. At the plugin level, set barrel: { type: 'named', nested: true } for hierarchical barrels, or barrel: false to opt out for that plugin.

Creating a custom middleware

Use defineMiddleware from @kubb/core. The example below writes a manifest.json that lists every plugin that ran and every file the build produced:

manifestMiddleware.ts
typescript
import {  } from '@kubb/core'
import { , ,  } from '@kubb/ast'

export const  = ((: { ?: `${string}.json` } = {}) => {
  const  = . ?? 'manifest.json'
  const  = new <string>()

  return {
    : 'middleware-manifest',
    : {
      'kubb:plugin:end'({  }) {
        .(.)
      },
      'kubb:plugins:end'({ , ,  }) {
        const  = {
          : [...],
          : .(() => .),
        }

        (
          ({
            : ,
            : `${..}/${}`,
            : [({ : [(.(, null, 2))] })],
          }),
        )
      },
    },
  }
})

Register it in kubb.config.ts:

kubb.config.ts
typescript
import {  } from 'kubb'
import {  } from './manifestMiddleware.ts'
Cannot find module './manifestMiddleware.ts' or its corresponding type declarations.
export default ({ : { : './petStore.yaml' }, : { : './src/gen' }, : [({ : 'kubb.manifest.json' })], })

Examples

Add a banner to every generated file

bannerMiddleware.ts
typescript
import {  } from '@kubb/core'

export const  = ((: { : string }) => ({
  : 'middleware-banner',
  : {
    'kubb:plugins:end'({  }) {
      for (const  of ) {
        . = `/* ${.} */\n${. ?? ''}`
      }
    },
  },
}))