@hey-api/openapi-ts
@hey-api/openapi-ts is an ESM code generator that reads an OpenAPI document and writes TypeScript artifacts such as request and response types, callable SDK functions, runtime schemas, framework query helpers, mocks, and server stubs. A plugin list controls both what is generated and which HTTP client shape it targets, including Fetch, Axios, Angular, Next.js, Nuxt, Ky, and ofetch. It is a build-time tool, not a runtime HTTP client by itself, and the quality of its output depends heavily on operation IDs and schemas in your API contract.
One of the broadest TypeScript-first OpenAPI generators, particularly when SDK, validation, query, and server plugins should share a contract. Adopt it with an exact version, generated-diff checks, and clear ownership of the OpenAPI document.
Use it if
- You own an OpenAPI contract and want typed SDK functions instead of hand-maintained fetch wrappers
- You want one generation step to produce types plus Zod, Valibot, TanStack Query, MSW, Fastify, NestJS, or oRPC helpers
- You need to select a client style such as Fetch, Axios, Angular, Next.js, Nuxt, Ky, or ofetch
- Your CI and developers can standardize on Node 22.18 or newer and pin the generator exactly
- You need a stable 1.x contract; the package is still at 0.99.0 and its own README says to pin an exact version because breaking releases are expected during initial development
- Your toolchain is on Node 20 or older; version 0.99.0 requires Node 22.18.0 or newer and has an ESM-only programmatic export
- You only need TypeScript types, not a generated SDK or plugin system; openapi-typescript is narrower and introduces less generated code
- Your OpenAPI descriptions have weak or unstable operation IDs and schema names; generated exports and diffs will inherit that churn
- You expect generated files to be hand-edited; the documentation explicitly treats the output directory as a dependency whose contents may be erased on regeneration
Setup reality
Install the generator as an exact-pinned dev dependency, for example `npm install -D -E @hey-api/openapi-ts@0.99.0`. The current package requires Node 22.18.0 or newer and peers on TypeScript 5.5.3 or newer, including supported TypeScript 6 releases. Its JavaScript API is ESM-only, although configuration may be TypeScript, ESM, CommonJS, or another format supported by the loader. A useful setup has `openapi-ts.config.ts` with input, output, and an explicit plugin list, plus an npm script that runs `openapi-ts`; checking generated files into source control is a team choice, but CI should regenerate and fail on diffs if reproducibility matters. Inputs can be a local JSON or YAML path, a URL, a Hey API Registry identifier, or an in-memory OpenAPI object. Remote private specs need whatever authentication the input endpoint expects, and the docs mention disabling TLS verification for self-signed development endpoints, which should never become a general production setting. Generation writes into the configured output folder and can erase manual changes, so treat that directory as generated code. Plugin choices can introduce runtime packages: the Fetch example is self-contained, while Axios output expects axios and TanStack Query output expects the matching TanStack package in the consuming application. Post-process commands such as formatters must also be installed and can make output vary across machines if their versions float. The project is pre-1.0 and publishes migration notes, so exact pinning plus review of generated diffs is not optional busywork. Spec changes can rename functions, change nullability, or alter request shapes even when the generator version stays fixed.
Patterns
Generate a client directly from the CLIgenerate-from-cli
npx @hey-api/openapi-ts@0.99.0 -i ./openapi.yaml -o ./src/clientPin the exact version in repeatable builds; the package is pre-1.0 and publishes breaking migration notes.
Create a typed generator configconfigure-local-spec
import { defineConfig } from '@hey-api/openapi-ts'
export default defineConfig({
input: './openapi.yaml',
output: './src/client',
})The default plugins generate TypeScript interfaces and an SDK; use an explicit plugin list when output must stay predictable.
Run the checked-in configurationadd-generation-script
{
"scripts": {
"generate:api": "openapi-ts"
}
}Keep the config at the project root or pass the CLI's config option when using a nonstandard location.
Generate from a Node scriptgenerate-programmatically
import { createClient } from '@hey-api/openapi-ts'
await createClient({
input: './openapi.yaml',
output: './src/client',
})The programmatic API is ESM-only and the package requires Node 22.18.0 or newer.
Generate a Fetch SDK and schemasgenerate-fetch-sdk
import { defineConfig } from '@hey-api/openapi-ts'
export default defineConfig({
input: './openapi.yaml',
output: './src/client',
plugins: [
'@hey-api/client-fetch',
'@hey-api/schemas',
'@hey-api/sdk',
'@hey-api/typescript',
],
})An explicit list replaces assumptions about default plugins and makes generated diffs easier to explain.
Target Axios instead of Fetchgenerate-axios-sdk
export default defineConfig({
input: './openapi.yaml',
output: './src/client',
plugins: [
'@hey-api/client-axios',
'@hey-api/sdk',
'@hey-api/typescript',
],
})Install axios in the consuming application because the generated Axios client depends on it at runtime.
Generate TanStack React Query helpersgenerate-react-query
export default defineConfig({
input: './openapi.yaml',
output: './src/client',
plugins: [
'@hey-api/client-fetch',
{ name: '@hey-api/sdk', instance: true },
'@hey-api/typescript',
'@tanstack/react-query',
],
})Install the matching @tanstack/react-query runtime package; plugin names distinguish framework-specific TanStack variants.
Post-process generated filesformat-generated-output
export default defineConfig({
input: './openapi.yaml',
output: {
path: './src/client',
postProcess: ['oxfmt', 'oxlint'],
},
})Every listed command must be installed and deterministic; floating formatter versions can create noisy generated diffs.
Set the generated service client defaultsconfigure-generated-client
import { client } from './client/client.gen'
client.setConfig({
baseUrl: 'https://api.example.com',
headers: { Authorization: `Bearer ${token}` },
})A global mutable client can leak per-user credentials in server applications; use request-scoped clients there.
Create an isolated generated client instancecreate-local-client
import { createClient } from './client/client'
const api = createClient({
baseUrl: 'https://api.example.com',
headers: { Authorization: `Bearer ${token}` },
})Pass this instance through an SDK call's client option when configuration must be scoped to a tenant or request.
Call a generated SDK operationcall-generated-operation
import { getPetById } from './client/sdk.gen'
const { data, error } = await getPetById({
path: { petId: 42 },
})
if (error) throw new Error('API request failed')
console.log(data)Function names and parameter groups come from the OpenAPI operation; inspect generated output rather than copying names from another spec.
Attach fresh credentials with an interceptoradd-auth-interceptor
client.interceptors.request.use((request) => {
request.headers.set('Authorization', `Bearer ${getToken()}`)
return request
})On a server, avoid reading user-specific tokens from shared global state; create or pass a request-scoped client.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| openapi-typescript | npm | Choose it when you want TypeScript types from OpenAPI with minimal generated runtime code |
| orval | npm | Choose it for a mature React Query and mock-oriented generator with a different configuration model |
| swagger-typescript-api | npm | Choose it for template-driven TypeScript API client generation and direct customization |
| @openapitools/openapi-generator-cli | npm | Choose it when you need the broad language and framework catalog of OpenAPI Generator and accept its heavier toolchain |