Import an OpenAPI spec in TypeScript with openapi-import
openapi-import lets you import a spec like a TypeScript module and get the output of every plugin in your kubb.config.ts, without generated files:
import { type Pet, petStatusEnum } from './petStore.openapi.yaml'It is a TypeScript content mapper, so tsc and your editor type-check the plugin output in place of the spec. Go to definition, hover, and rename jump between the generated types and the keys in the spec.
openapi-import is experimental. It needs TypeScript 7.1.0-beta or later with --runExternalCode.Install openapi-import
Install it with Kubb and the TypeScript beta:
pnpm add -D kubb openapi-import typescript@betanpm install -D kubb openapi-import typescript@betaRegister the extension
Add the content mapper to tsconfig.json, and name the spec <name>.openapi.yaml:
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"noEmit": true
},
"contentMappers": [{ "package": "openapi-import", "extensions": [".openapi.yaml", ".openapi.yml"] }],
"include": ["./src/**/*"]
}Run the type check
pnpm tsc --noEmit --runExternalCodenode node_modules/typescript/bin/tsc --noEmit --runExternalCodeWith npm, a bare tsc runs TypeScript 6, so call the binary from typescript.
VS Code only passes --runExternalCode in trusted workspaces.
Choose the plugins
Add a kubb.config.ts to set plugin options or to run other plugins, and install each plugin it imports:
pnpm add -D @kubb/plugin-tsimport { pluginTs } from '@kubb/plugin-ts'
import { defineConfig } from 'kubb/config'
export default defineConfig({
output: { path: './src/gen' },
plugins: [pluginTs()],
})Add ./kubb.config.ts to include in tsconfig.json. The mapper finds the config like the CLI does and reloads it when it changes. With several configs, a spec uses the one whose input matches it.
Load the spec at run time
TypeScript does not emit the spec to JavaScript, so values such as petStatusEnum need a bundler. Add kubb() from any build tool integration without a config option, so it reads the same kubb.config.ts as tsc:
import kubb from 'kubb/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [kubb()],
})The types and the values then come from the same import:
import { type GetPetByIdPath, type GetPetByIdResponse, petStatusEnum } from '../petStore.openapi.yaml'
export async function getAvailablePet({ petId }: GetPetByIdPath): Promise<GetPetByIdResponse | undefined> {
const response = await fetch(`https://petstore3.swagger.io/api/v3/pet/${petId}`)
const pet: GetPetByIdResponse = await response.json()
return pet.status === petStatusEnum.available ? pet : undefined
}A config with an input also writes the generated files at build time. Leave input out to import specs only. The bundler reads kubb.config.ts once, so restart it after you change the config.
Limits
- Code a plugin writes to a separate file, such as a bundled client runtime, is not included. Point imports like the client's
importPathat a package. - Files that
kubb.config.tsimports are not watched. Restart the TypeScript server after you change them. - A changed spec runs a full Kubb build: about 0.4 s for a 10 KB spec and 2.6 s for a 1.4 MB spec.
See also
- Build tool integrations
- Vite integration
- Configuration reference
openapi-importon GitHub, with a working pet-store example