openapi-ts Documentation

‍🚀 Turn API specifications into production-ready SDKs, validators, mocks, and more. 20+ plugins. Millions of weekly npm downloads.

RAW Doc

Technical Documentation: hey-api/openapi-ts

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (`README.md`)

About

Hey API is an ecosystem for turning API specifications into production-ready code. It's trusted by thousands of companies – from YC startups to Fortune 500 enterprises – and powers products used by millions worldwide.

This is the monorepo containing all open source Hey API packages. For package-specific documentation and quick start guides, see the individual repositories below.

Packages

Package Description
openapi-ts TypeScript code generator with 20+ plugins for SDKs, Zod schemas, TanStack Query hooks, and more.
text
[Manual](https://heyapi.dev/docs/openapi/typescript/get-started)
   • 
  [npm](https://npmjs.com/package/@hey-api/openapi-ts) |

| openapi-python | Python code generator for SDKs and Pydantic models.

text
[Manual](https://heyapi.dev/docs/openapi/python/get-started)
   • 
  [npm](https://npmjs.com/package/@hey-api/openapi-python) |

Highlights

  • production-grade code that compiles
  • runs in any Node.js 22+ environment
  • accepts any OpenAPI specification
  • core plugins for SDKs
  • HTTP clients for Fetch API, Axios, Angular, Next.js, Nuxt, HTTPX, and more
  • Zod schemas, TanStack Query hooks, Pydantic models, and 20+ plugins to reduce third-party boilerplate
  • highly customizable via plugins

Libraries

Package Description
spec-types TypeScript definitions for OpenAPI and JSON Schema specifications with complete inline documentation.
text
[npm](https://npmjs.com/package/@hey-api/spec-types) |

Contributing

Want to see your code in products used by millions?

Start with our Contributing guide and release your first feature.

Sponsors

Partners behind the future of API tooling. Become a sponsor.

Gold

Silver

fastapi.tiangolo.com getunblocked.com

Bronze

License

Released under the MIT License.


Overview (`.changeset/README.md`)

Changesets

Hello and welcome! This folder has been automatically generated by @changesets/cli, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it in our repository

We have a quick list of common questions to get you started engaging with this project in
our documentation


Overview (`examples/README.md`)

Examples

This directory contains integration examples demonstrating how to use @hey-api/openapi-ts with various frameworks, libraries, and client implementations.

Available Examples

  • openapi-ts-angular - Angular integration with common HTTP client
  • openapi-ts-angular-common - Angular with @angular/common/http
  • openapi-ts-axios - Using Axios client
  • openapi-ts-fastify - Fastify server integration
  • openapi-ts-fetch - Native Fetch API client
  • openapi-ts-next - Next.js integration
  • openapi-ts-nuxt - Nuxt.js integration with plugin
  • openapi-ts-ofetch - Using ofetch client
  • openapi-ts-openai - OpenAI API integration
  • openapi-ts-pinia-colada - Vue with Pinia Colada state management
  • openapi-ts-sample - Sample/template example (excluded from CI)
  • openapi-ts-tanstack-angular-query-experimental - Angular with TanStack Query
  • openapi-ts-tanstack-react-query - React with TanStack Query
  • openapi-ts-tanstack-svelte-query - Svelte with TanStack Query
  • openapi-ts-tanstack-vue-query - Vue with TanStack Query

Generated Code

All examples (except openapi-ts-sample) contain generated client code that is committed to the repository. This ensures:

  1. Examples always reflect the current state of the code generator
  2. Changes to the code generator are visible in pull requests
  3. CI can verify that examples are kept up-to-date

Regenerating Examples

After making changes to the core packages, regenerate all example code:

bash
pnpm examples:update

This command will run openapi-ts for each example that has an openapi-ts script in its package.json.

Verifying Examples

To check if all examples are up-to-date with the current codebase:

bash
pnpm examples:check

This check is also run automatically in CI. If it fails, run pnpm examples:update and commit the changes.

Running Examples

Each example can be run individually using the example script:

bash
# Run dev server for fetch example
pnpm example fetch dev

# Build fetch example
pnpm example fetch build

Or directly using pnpm filters:

bash
pnpm --filter @example/openapi-ts-fetch dev

Creating New Examples

When creating a new example:

  1. Create a new directory in examples/
  2. Add an openapi-ts script to package.json
  3. Run pnpm examples:update to create initial generated code
  4. Commit both the source and generated code
  5. The example will automatically be included in CI checks

Excluding Examples

To exclude an example from CI (like openapi-ts-sample):

  1. Remove the openapi-ts script from package.json, or
  2. Update the exclusion filters in package.json scripts and .github/workflows/ci.yml

Overview (`examples/openapi-ts-angular-common/README.md`)

Angular

This project was generated using Angular CLI version 19.2.15.

Development server

To start a local development server, run:

bash
ng serve

Once the server is running, open your browser and navigate to http://localhost:4200/. The application will automatically reload whenever you modify any of the source files.

Code scaffolding

Angular CLI includes powerful code scaffolding tools. To generate a new component, run:

bash
ng generate component component-name

For a complete list of available schematics (such as components, directives, or pipes), run:

bash
ng generate --help

Building

To build the project run:

bash
ng build

This will compile your project and store the build artifacts in the dist/ directory. By default, the production build optimizes your application for performance and speed.

Running unit tests

To execute unit tests with the Karma test runner, use the following command:

bash
ng test

Running end-to-end tests

For end-to-end (e2e) testing, run:

bash
ng e2e

Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.

Additional Resources

For more information on using the Angular CLI, including detailed command references, visit the Angular CLI Overview and Command Reference page.


Overview (`examples/openapi-ts-angular/README.md`)

Angular

This project was generated using Angular CLI version 19.2.15.

Development server

To start a local development server, run:

bash
ng serve

Once the server is running, open your browser and navigate to http://localhost:4200/. The application will automatically reload whenever you modify any of the source files.

Code scaffolding

Angular CLI includes powerful code scaffolding tools. To generate a new component, run:

bash
ng generate component component-name

For a complete list of available schematics (such as components, directives, or pipes), run:

bash
ng generate --help

Building

To build the project run:

bash
ng build

This will compile your project and store the build artifacts in the dist/ directory. By default, the production build optimizes your application for performance and speed.

Running unit tests

To execute unit tests with the Karma test runner, use the following command:

bash
ng test

Running end-to-end tests

For end-to-end (e2e) testing, run:

bash
ng e2e

Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.

Additional Resources

For more information on using the Angular CLI, including detailed command references, visit the Angular CLI Overview and Command Reference page.


Overview (`examples/openapi-ts-next/README.md`)

This is a Next.js project bootstrapped with create-next-app.

Getting Started

First, run the development server:

bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev

Open http://localhost:3000 with your browser to see the result.

You can start editing the page by modifying app/page.tsx. The page auto-updates as you edit the file.

This project uses next/font to automatically optimize and load Geist, a new font family for Vercel.

Learn More

To learn more about Next.js, take a look at the following resources:

You can check out the Next.js GitHub repository - your feedback and contributions are welcome!

Deploy on Vercel

The easiest way to deploy your Next.js app is to use the Vercel Platform from the creators of Next.js.

Check out our Next.js deployment documentation for more details.


Overview (`examples/openapi-ts-nuxt/README.md`)

Nuxt Minimal Starter

Look at the Nuxt documentation to learn more.

Setup

Make sure to install dependencies:

bash
# npm
npm install

# pnpm
pnpm install

# yarn
yarn install

# bun
bun install

Development Server

Start the development server on http://localhost:3000:

bash
# npm
npm run dev

# pnpm
pnpm dev

# yarn
yarn dev

# bun
bun run dev

Production

Build the application for production:

bash
# npm
npm run build

# pnpm
pnpm build

# yarn
yarn build

# bun
bun run build

Locally preview production build:

bash
# npm
npm run preview

# pnpm
pnpm preview

# yarn
yarn preview

# bun
bun run preview

Check out the deployment documentation for more information.


Overview (`examples/openapi-ts-pinia-colada/README.md`)

@hey-api/openapi-ts with Pinia Colada Example

This example demonstrates how to use @hey-api/openapi-ts with Pinia Colada for Vue 3 applications.

VSCode + Volar (and disable Vetur).

Type Support for `.vue` Imports in TS

TypeScript cannot handle type information for .vue imports by default, so we replace the tsc CLI with vue-tsc for type checking. In editors, we need Volar to make the TypeScript language service aware of .vue types.

Customize configuration

See Vite Configuration Reference.

Project Setup

sh
pnpm install

Compile and Hot-Reload for Development

sh
pnpm dev

Type-Check, Compile and Minify for Production

sh
pnpm build

Run Unit Tests with [Vitest](https://vitest.dev)

sh
pnpm test:unit

Overview (`examples/openapi-ts-tanstack-angular-query-experimental/README.md`)

OpenapiTsTanstackAngularQueryExperimental

This project was generated with Angular CLI version 18.2.7.

Development server

Run ng serve for a dev server. Navigate to http://localhost:4200/. The application will automatically reload if you change any of the source files.

Code scaffolding

Run ng generate component component-name to generate a new component. You can also use ng generate directive|pipe|service|class|guard|interface|enum|module.

Build

Run ng build to build the project. The build artifacts will be stored in the dist/ directory.

Running unit tests

Run ng test to execute the unit tests via Karma.

Running end-to-end tests

Run ng e2e to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.

Further help

To get more help on the Angular CLI use ng help or go check out the Angular CLI Overview and Command Reference page.


Overview (`examples/openapi-ts-tanstack-svelte-query/README.md`)

create-svelte

Everything you need to build a Svelte project, powered by create-svelte.

Creating a project

If you're seeing this, you've probably already done this step. Congrats!

bash
# create a new project in the current directory
npm create svelte@latest

# create a new project in my-app
npm create svelte@latest my-app

Developing

Once you've created a project and installed dependencies with npm install (or pnpm install or yarn), start a development server:

bash
npm run dev

# or start the server and open the app in a new browser tab
npm run dev -- --open

Building

To create a production version of your app:

bash
npm run build

You can preview the production build with npm run preview.

To deploy your app, you may need to install an adapter for your target environment.


Overview (`examples/openapi-ts-tanstack-vue-query/README.md`)

vue-project

This template should help get you started developing with Vue 3 in Vite.

VSCode + Volar (and disable Vetur).

Type Support for `.vue` Imports in TS

TypeScript cannot handle type information for .vue imports by default, so we replace the tsc CLI with vue-tsc for type checking. In editors, we need Volar to make the TypeScript language service aware of .vue types.

Customize configuration

See Vite Configuration Reference.

Project Setup

sh
pnpm install

Compile and Hot-Reload for Development

sh
pnpm dev

Type-Check, Compile and Minify for Production

sh
pnpm build

Run Unit Tests with [Vitest](https://vitest.dev)

sh
pnpm test:unit

Overview (`packages/codegen-core/README.md`)

Notice

This is an internal framework. Visit Hey API to discover our products.

Contributing

Want to see your code in products used by millions?

Start with our Contributing guide and release your first feature.

Sponsors

Partners behind the future of API tooling. Become a sponsor.

Gold

Silver

fastapi.tiangolo.com getunblocked.com

Bronze

License

Released under the MIT License.


Overview (`packages/json-schema-ref-parser/README.md`)

JSON Schema $Ref Parser

Parse, Resolve, and Dereference JSON Schema $ref pointers

Installation

Install using npm:

bash
npm install @hey-api/json-schema-ref-parser
pnpm add @hey-api/json-schema-ref-parser
yarn add @hey-api/json-schema-ref-parser
bun add @hey-api/json-schema-ref-parser

The Problem:

You've got a JSON Schema with $ref pointers to other files and/or URLs. Maybe you know all the referenced files ahead
of time. Maybe you don't. Maybe some are local files, and others are remote URLs. Maybe they are a mix of JSON and YAML
format. Maybe some of the files contain cross-references to each other.

json
{
  "definitions": {
    "person": {
      // references an external file
      "$ref": "schemas/people/Bruce-Wayne.json"
    },
    "place": {
      // references a sub-schema in an external file
      "$ref": "schemas/places.yaml#/definitions/Gotham-City"
    },
    "thing": {
      // references a URL
      "$ref": "http://wayne-enterprises.com/things/batmobile"
    },
    "color": {
      // references a value in an external file via an internal reference
      "$ref": "#/definitions/thing/properties/colors/black-as-the-night"
    }
  }
}

The Solution:

JSON Schema $Ref Parser is a full JSON Reference
and JSON Pointer implementation that crawls even the most
complex JSON Schemas and gives you simple, straightforward
JavaScript objects.

  • Use JSON or YAML schemas — or even a mix of both!
  • Supports $ref pointers to external files and URLs, as well as custom sources such as databases
  • Can bundle multiple files into a single schema that only has internal $ref pointers
  • Can dereference your schema, producing a plain-old JavaScript object that's easy to work with
  • Supports circular references, nested references,
    back-references, and cross-references between files
  • Maintains object reference equality — $ref pointers to the same value always resolve to the same object
    instance
  • Compatible with Node LTS and beyond, and all major web browsers on Windows, Mac, and Linux

New in this fork (@hey-api)

  • Multiple inputs with bundleMany: Merge and bundle several OpenAPI/JSON Schema inputs (files, URLs, or raw objects) into a single schema. Components are prefixed to avoid name collisions, paths are namespaced on conflict, and $refs are rewritten accordingly.
javascript
import { $RefParser } from '@hey-api/json-schema-ref-parser';

const parser = new $RefParser();
const merged = await parser.bundleMany({
  pathOrUrlOrSchemas: [
    './specs/a.yaml',
    'https://example.com/b.yaml',
    { openapi: '3.1.0', info: { title: 'Inline' }, paths: {} },
  ],
});

// merged.components.* will contain prefixed names like a_<name>, b_<name>, etc.
  • Dereference hooks: Fine-tune dereferencing with excludedPathMatcher(path) => boolean to skip subpaths and onDereference(path, value, parent, parentPropName) to observe replacements.
javascript
const parser = new $RefParser();
parser.options.dereference.excludedPathMatcher = (p) => p.includes('/example/');
parser.options.dereference.onDereference = (p, v) => {
  // inspect p / v as needed
};
await parser.dereference({ pathOrUrlOrSchema: './openapi.yaml' });
  • Smart input resolution: You can pass a file path, URL, or raw schema object. If a raw schema includes $id, it is used as the base URL for resolving relative $refs.
javascript
await new $RefParser().bundle({
  pathOrUrlOrSchema: {
    $id: 'https://api.example.com/openapi.json',
    openapi: '3.1.0',
    paths: {
      '/ping': { get: { responses: { 200: { description: 'ok' } } } },
    },
  },
});

Contributing

Want to see your code in products used by millions?

Start with our Contributing guide and release your first feature.

Sponsors

Partners behind the future of API tooling. Become a sponsor.

Gold

Silver

fastapi.tiangolo.com getunblocked.com

Bronze

License

Released under the MIT License.


Overview (`packages/nuxt/README.md`)

Features

  • seamless integration with @hey-api/openapi-ts ecosystem
  • type-safe response data and errors
  • response data validation and transformation
  • access to the original request and response
  • granular request and response customization options
  • minimal learning curve thanks to extending the underlying technology
  • support bundling inside the generated output
  • platform for automating codegen builds

Dashboard

Access your projects and OpenAPI specifications in the Hey API Platform.

Contributing

Want to see your code in products used by millions?

Start with our Contributing guide and release your first feature.

Documentation

Please visit our website for documentation, guides, migrating, and more.

Sponsors

Partners behind the future of API tooling. Become a sponsor.

Gold

Silver

fastapi.tiangolo.com getunblocked.com

Bronze

Migrating

You can learn more on the Migrating page.

License

Released under the MIT License.


Overview (`packages/openapi-python/README.md`)

About

The OpenAPI to Python code generator.

Generate production-grade SDKs and Pydantic models.

Part of the Hey API ecosystem.

Features

  • production-grade code that compiles
  • runs in any Node.js 22+ environment
  • accepts any OpenAPI specification
  • core plugin for SDKs
  • HTTP client for HTTPX
  • highly customizable via plugins
  • sync with Hey API Registry for spec management

Contributing

Want to see your code in products used by millions?

Start with our Contributing guide and release your first feature.

Sponsors

Partners behind the future of API tooling. Become a sponsor.

Gold

Silver

fastapi.tiangolo.com getunblocked.com

Bronze

Quick Start

The fastest way to use @hey-api/openapi-python is via npx

sh
npx @hey-api/openapi-python -i hey-api/backend -o src/client

Congratulations on creating your first client! 🎉 You can learn more about the generated files on the Output page.

Installation

You can download @hey-api/openapi-python from npm using your favorite package manager.

npm

sh
npm install @hey-api/openapi-python -D -E

pnpm

sh
pnpm add @hey-api/openapi-python -D -E

yarn

sh
yarn add @hey-api/openapi-python -D -E

bun

sh
bun add @hey-api/openapi-python -D

Versioning

This package is in initial development. Please pin an exact version so you can safely upgrade when you're ready.

We publish migration notes for every breaking release. You might not be impacted by a breaking change if you don't use the affected features.

Usage

CLI

Most people run @hey-api/openapi-python via CLI. To do that, add a script to your package.json file which will make openapi-python executable through script.

json
"scripts": {
  "openapi-python": "openapi-python"
}

The above script can be executed by running npm run openapi-python or equivalent command in other package managers. Next, we will create a configuration file and move our options from Quick Start to it.

Node.js

You can also generate output programmatically by calling createClient() in a JavaScript/TypeScript file.

`script.ts`

ts
import { createClient } from '@hey-api/openapi-python';

createClient({
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
});

Configuration

@hey-api/openapi-python supports loading configuration from any file inside your project root folder supported by jiti loader. Below are the most common file formats.

`openapi-python.config.ts`

js
import { defineConfig } from '@hey-api/openapi-python';

export default defineConfig({
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
});

`openapi-python.config.cjs`

js
/** @type {import('@hey-api/openapi-python').UserConfig} */
module.exports = {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
};

`openapi-python.config.mjs`

js
/** @type {import('@hey-api/openapi-python').UserConfig} */
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
};

Alternatively, you can use openapi-python.config.js and configure the export statement depending on your project setup.

Input

You must set the input so we can load your OpenAPI specification. It can be a path or URL, object containing a path or URL, or an object representing an OpenAPI specification. Hey API supports all valid OpenAPI versions and file formats.

If you use an HTTPS URL with a self-signed certificate in development, you will need to set NODE_TLS_REJECT_UNAUTHORIZED=0 in your environment.

Output

You must set the output so we know where to generate your files. It can be a path to the destination folder or an object containing the destination folder path and optional settings.

You should treat the output folder as a dependency. Do not directly modify its contents as your changes might be erased when you run @hey-api/openapi-python again.

Parser

We parse your input before making it available to plugins. While configuring the parser is optional, it's the perfect place to modify or validate your input if needed.

Plugins

Plugins are responsible for generating artifacts from your input. By default, Hey API will generate Pydantic models and SDK from your OpenAPI specification. You can add, remove, or customize any of the plugins. In fact, we highly encourage you to do so!

Client

Clients are responsible for sending the actual HTTP requests. We default to HTTPX client if you're generating SDKs, but you can choose a different option from the available clients.

Available Clients

Proposed Clients (Vote to Prioritize)

The following clients are roadmap proposals and are not started yet. You can help us prioritize them by voting on GitHub.

Don't see your client? Build your own or let us know your interest by opening an issue.

Available Plugins

These plugins help reduce boilerplate associated with third-party dependencies. Hey API natively supports the most popular packages. Please open an issue on GitHub if you'd like us to support your favorite package.

Proposed Plugins (Vote to Prioritize)

The following plugins are roadmap proposals and are not started yet. You can help us prioritize them by voting on GitHub.

Don't see your plugin? Build your own or let us know your interest by opening an issue.

Migrating

You can learn more on the Migrating page.

License

Released under the MIT License.


Overview (`packages/openapi-ts/README.md`)

About

The OpenAPI to TypeScript code generator used by Vercel, OpenCode, and PayPal.

Generate production-grade SDKs, Zod schemas, TanStack Query hooks, or choose from 20+ other plugins.

Part of the Hey API ecosystem.

Features

  • production-grade code that compiles
  • runs in any Node.js 22+ environment
  • accepts any OpenAPI specification
  • core plugins for SDKs, types, and schemas
  • HTTP clients for Fetch API, Angular, Axios, Next.js, Nuxt, and more
  • 20+ plugins to reduce third-party boilerplate
  • highly customizable via plugins
  • sync with Hey API Registry for spec management

Contributing

Want to see your code in products used by millions?

Start with our Contributing guide and release your first feature.

Sponsors

Partners behind the future of API tooling. Become a sponsor.

Gold

Silver

fastapi.tiangolo.com getunblocked.com

Bronze

Quick Start

The fastest way to use @hey-api/openapi-ts is via npx

sh
npx @hey-api/openapi-ts -i hey-api/backend -o src/client

Congratulations on creating your first client! 🎉 You can learn more about the generated files on the Output page.

Installation

You can download @hey-api/openapi-ts from npm using your favorite package manager.

npm

sh
npm install @hey-api/openapi-ts -D -E

pnpm

sh
pnpm add @hey-api/openapi-ts -D -E

yarn

sh
yarn add @hey-api/openapi-ts -D -E

bun

sh
bun add @hey-api/openapi-ts -D

Versioning

This package is in initial development. Please pin an exact version so you can safely upgrade when you're ready.

We publish migration notes for every breaking release. You might not be impacted by a breaking change if you don't use the affected features.

Usage

CLI

Most people run @hey-api/openapi-ts via CLI. To do that, add a script to your package.json file which will make openapi-ts executable through script.

json
"scripts": {
  "openapi-ts": "openapi-ts"
}

The above script can be executed by running npm run openapi-ts or equivalent command in other package managers. Next, we will create a configuration file and move our options from Quick Start to it.

Node.js

You can also generate output programmatically by calling createClient() in a JavaScript/TypeScript file.

`script.ts`

ts
import { createClient } from '@hey-api/openapi-ts';

createClient({
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
});

Vite

If you're using Vite, you can integrate @hey-api/openapi-ts directly into your build pipeline with @hey-api/vite-plugin. Install it alongside the main package:

npm

sh
npm install @hey-api/vite-plugin -D -E

pnpm

sh
pnpm add @hey-api/vite-plugin -D -E

yarn

sh
yarn add @hey-api/vite-plugin -D -E

bun

sh
bun add @hey-api/vite-plugin -D

Then add the plugin to your Vite configuration:

`vite.config.ts`

ts
import { heyApiPlugin } from '@hey-api/vite-plugin';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    heyApiPlugin({
      config: {
        input: 'hey-api/backend', // sign up at app.heyapi.dev
        output: 'src/client',
      },
    }),
  ],
});

See the Vite page for full configuration options.

Configuration

@hey-api/openapi-ts supports loading configuration from any file inside your project root folder supported by jiti loader. Below are the most common file formats.

`openapi-ts.config.ts`

js
import { defineConfig } from '@hey-api/openapi-ts';

export default defineConfig({
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
});

`openapi-ts.config.cjs`

js
/** @type {import('@hey-api/openapi-ts').UserConfig} */
module.exports = {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
};

`openapi-ts.config.mjs`

js
/** @type {import('@hey-api/openapi-ts').UserConfig} */
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
};

Alternatively, you can use openapi-ts.config.js and configure the export statement depending on your project setup.

Input

You must set the input so we can load your OpenAPI specification. It can be a path or URL, object containing a path or URL, or an object representing an OpenAPI specification. Hey API supports all valid OpenAPI versions and file formats.

If you use an HTTPS URL with a self-signed certificate in development, you will need to set NODE_TLS_REJECT_UNAUTHORIZED=0 in your environment.

Output

You must set the output so we know where to generate your files. It can be a path to the destination folder or an object containing the destination folder path and optional settings.

You should treat the output folder as a dependency. Do not directly modify its contents as your changes might be erased when you run @hey-api/openapi-ts again.

Parser

We parse your input before making it available to plugins. While configuring the parser is optional, it's the perfect place to modify or validate your input if needed.

Plugins

Plugins are responsible for generating artifacts from your input. By default, Hey API will generate TypeScript interfaces and SDK from your OpenAPI specification. You can add, remove, or customize any of the plugins. In fact, we highly encourage you to do so!

Client

Clients are responsible for sending the actual HTTP requests. We default to Fetch client if you're generating SDKs, but you can choose a different option from the available clients.

Available Clients

Proposed Clients (Vote to Prioritize)

The following clients are roadmap proposals and are not started yet. You can help us prioritize them by voting on GitHub.

Don't see your client? Build your own or let us know your interest by opening an issue.

Available Plugins

These plugins help reduce boilerplate associated with third-party dependencies. Hey API natively supports the most popular packages. Please open an issue on GitHub if you'd like us to support your favorite package.

Proposed Plugins (Vote to Prioritize)

The following plugins are roadmap proposals and are not started yet. You can help us prioritize them by voting on GitHub.

Don't see your plugin? Build your own or let us know your interest by opening an issue.

Migrating

You can learn more on the Migrating page.

License

Released under the MIT License.


Overview (`packages/spec-types/README.md`)

About

TypeScript definitions for OpenAPI and JSON Schema specifications with complete inline documentation.

Part of the Hey API ecosystem.

Features

  • OpenAPI 2.0 (Swagger), 3.0, and 3.1 specifications
  • JSON Schema Draft 4 and Draft 2020-12
  • inline JSDoc on every property
  • zero runtime footprint, types only

Contributing

Want to see your code in products used by millions?

Start with our Contributing guide and release your first feature.

Sponsors

Partners behind the future of API tooling. Become a sponsor.

Gold

Silver

fastapi.tiangolo.com getunblocked.com

Bronze

Installation

You can download @hey-api/spec-types from npm using your favorite package manager.

npm

sh
npm install @hey-api/spec-types -D -E

pnpm

sh
pnpm add @hey-api/spec-types -D -E

yarn

sh
yarn add @hey-api/spec-types -D -E

bun

sh
bun add @hey-api/spec-types -D

Usage

OpenAPI

Hover over any property to see its definition from the specification.

ts
import type { OpenAPIV3_1 } from '@hey-api/spec-types';

const doc = {
  openapi: '3.1.2',
  info: {
    title: 'My OpenAPI document',
    version: '1.0.0',
  },
} satisfies OpenAPIV3_1.Document;

JSON Schema

Full type coverage for Draft 4 and Draft 2020-12 schemas.

ts
import type { JSONSchemaDraft2020_12 } from '@hey-api/spec-types';

const doc = {
  properties: {
    foo: {
      type: 'string',
    },
  },
  type: 'object',
} satisfies JSONSchemaDraft2020_12.Document;

License

Released under the MIT License.


Overview (`packages/vite-plugin/README.md`)

About

Vite is a blazing fast frontend build tool powering the next generation of web applications.

The Vite plugin integrates @hey-api/openapi-ts into the Vite build pipeline, running automatically whenever Vite resolves its configuration – no separate script or manual step required.

Part of the Hey API ecosystem.

Features

  • runs automatically as part of your Vite build
  • reads your existing configuration (or accepts inline config)
  • supports Vite 5, 6, 7, and 8
  • sync with Hey API Registry for spec management

Contributing

Want to see your code in products used by millions?

Start with our Contributing guide and release your first feature.

Sponsors

Partners behind the future of API tooling. Become a sponsor.

Gold

Silver

fastapi.tiangolo.com getunblocked.com

Bronze

Installation

You can download @hey-api/vite-plugin from npm using your favorite package manager.

npm

sh
npm install @hey-api/vite-plugin -D -E

pnpm

sh
pnpm add @hey-api/vite-plugin -D -E

yarn

sh
yarn add @hey-api/vite-plugin -D -E

bun

sh
bun add @hey-api/vite-plugin -D

Usage

Add the plugin to your vite.config.ts:

ts
import { heyApiPlugin } from '@hey-api/vite-plugin';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [heyApiPlugin()],
});

The plugin will automatically pick up your configuration file. You can also pass options inline using the config option:

ts
import { heyApiPlugin } from '@hey-api/vite-plugin';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    heyApiPlugin({
      config: {
        input: 'hey-api/backend', // sign up at app.heyapi.dev
        output: 'src/client',
      },
    }),
  ],
});

License

Released under the MIT License.


Overview (`packages/openapi-python/pypi/README.md`)

Hey API

OpenAPI to Python code generator. Production-grade SDKs and Pydantic models. Built by Hey API.

Installation

uv

Install:

sh
uv add hey-api

Or run without installing:

sh
uvx hey-api --help

pip

Install:

sh
pip install hey-api

Or run without installing:

sh
pipx run hey-api

Usage

sh
hey-api --help

Resources


Web Src Content Docs Index (`web/src/content/docs/index.mdx`)


title: OpenAPI to code in seconds. template: splash

import Page from '@/components/pages/codegen/page.astro';


Web Src Content Docs Sponsors (`web/src/content/docs/sponsors.mdx`)


title: Sponsors template: splash

import SponsorsPage from '@/components/pages/sponsors/page.astro';


Web Src Content Docs Codegen Openapi Python (`web/src/content/docs/codegen/openapi/python.mdx`)


title: OpenAPI to Python in seconds. template: splash

import Page from '@/components/pages/codegen/page.astro';


Web Src Content Docs Codegen Openapi Typescript (`web/src/content/docs/codegen/openapi/typescript.mdx`)


title: OpenAPI to TypeScript in seconds. template: splash

import Page from '@/components/pages/codegen/page.astro';


Web Src Content Docs Docs Openapi Python Configuration (`web/src/content/docs/docs/openapi/python/configuration.mdx`)


title: Configuration sidebar: label: Overview

import { FileTree, Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import InputTabs from '@/components/code-groups/InputTabs.mdx';
import OutputTabs from '@/components/code-groups/OutputTabs.mdx';
import UserConfig from '@/components/UserConfig.astro';

Configuration

@hey-api/openapi-python supports loading configuration from any file inside your project root folder supported by jiti loader. Below are the most common file formats.

```ts import { defineConfig } from '@hey-api/openapi-python';

export default defineConfig({
input: 'hey-api/backend', // sign up at app.heyapi.dev
output: 'src/client',
});

text
</TabItem>
  <TabItem label="openapi-python.config.cjs">
```js
/** @type {import('@hey-api/openapi-python').UserConfig} */
module.exports = {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
};
```js /** @type {import('@hey-api/openapi-python').UserConfig} */ export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', }; ```

Alternatively, you can use openapi-python.config.js and configure the export statement depending on your project setup.

Input

You must provide an input so we can load your OpenAPI specification.

The input can be a string path, URL, API registry shorthand, an object containing any of these, or an object representing an OpenAPI specification. Hey API supports all valid OpenAPI versions and file formats.

You can learn more on the Input page.

:::tip
If you use an HTTPS URL with a self-signed certificate in development, you will need to set NODE_TLS_REJECT_UNAUTHORIZED=0 in your environment.
:::

Output

You must set the output so we know where to generate your files. It can be a path to the destination folder or an object containing the destination folder path and optional settings.

You can learn more on the Output page.

:::tip
You should treat the output folder as a dependency. Do not directly modify its contents as your changes might be erased when you run @hey-api/openapi-python again.
:::

Parser

We parse your input before making it available to plugins. Configuring the parser is optional, but it provides an ideal opportunity to modify or validate your input as needed.

You can learn more on the Parser page.

Plugins

Plugins are responsible for generating artifacts from your input. By default, Hey API will generate Pydantic models and SDK from your OpenAPI specification. You can add, remove, or customize any of the plugins. In fact, we highly encourage you to do so!

You can learn more on the Output page.

Advanced

More complex configuration scenarios can be handled by providing an array of inputs, outputs, or configurations.

Multiple jobs

Throughout this documentation, we generally reference single job configurations. However, you can easily run multiple jobs by providing an array of configuration objects.

```js export default [ { input: 'foo.yaml', output: 'src/foo', }, { input: 'bar.yaml', output: 'src/bar', }, ]; ``` - src - foo - client/ - core/ - client.gen.ts - index.ts - sdk.gen.ts - types.gen.ts - bar - client/ - core/ - client.gen.ts - index.ts - sdk.gen.ts - types.gen.ts

Job matrix

Reusing configuration across multiple jobs is possible by defining a job matrix. You can create a job matrix by providing input and output arrays of matching length.

```js export default { input: ['foo.yaml', 'bar.yaml'], output: ['src/foo', 'src/bar'], }; ``` - src - foo - client/ - core/ - client.gen.ts - index.ts - sdk.gen.ts - types.gen.ts - bar - client/ - core/ - client.gen.ts - index.ts - sdk.gen.ts - types.gen.ts

Merging inputs

You can merge inputs by defining multiple inputs and a single output.

```js export default { input: ['foo.yaml', 'bar.yaml'], output: 'src/client', }; ``` - src - client - client/ - core/ - client.gen.ts - index.ts - sdk.gen.ts - types.gen.ts

{/* */}


Web Src Content Docs Docs Openapi Python Get Started (`web/src/content/docs/docs/openapi/python/get-started.mdx`)


title: Get Started

import InstallCommand from '@/components/package-managers/InstallCommand.astro';
import Examples from '@/components/Examples.astro';
import StackBlitz from '@/components/StackBlitz.astro';

Get Started

@hey-api/openapi-python generates Python code from OpenAPI specifications. Point it at your spec, pick your plugins, and get production-grade code in seconds.

{/* Used by companies like Vercel, OpenCode, and PayPal. */}

“The ecosystem trusted by TypeScript developers is coming to Python codebases.”

— Lubos, Founder of Hey API

{/* ### Demo

<StackBlitz
example="hey-api-example"
openFile={[
'openapi-ts.config.ts',
'src/client/schemas.gen.ts',
'src/client/sdk.gen.ts',
'src/client/types.gen.ts',
]}
/> */}

Features

  • production-grade code that compiles
  • runs in any Node.js 22+ environment
  • accepts any OpenAPI specification
  • core plugin for SDKs
  • HTTP client for HTTPX
  • highly customizable via plugins
  • sync with Hey API Registry for spec management
    {/* - 20+ plugins to reduce third-party boilerplate */}

Quick Start

The fastest way to use @hey-api/openapi-python is via npx

sh
npx @hey-api/openapi-python -i hey-api/backend -o src/client

Congratulations on creating your first client! 🎉 You can learn more about the generated files on the Output page.

Installation

You can download @hey-api/openapi-python from npm using your favorite package manager.

Versioning

This package is in initial development. Please pin an exact version so you can safely upgrade when you're ready.

We publish migration notes for every breaking release. You might not be impacted by a breaking change if you don't use the affected features.

Usage

CLI

Most people run @hey-api/openapi-python via CLI. To do that, add a script to your package.json file which will make openapi-python executable through script.

json title="package.json"
"scripts": {
  "openapi-python": "openapi-python"
}

The above script can be executed by running npm run openapi-python or equivalent command in other package managers. Next, we will create a configuration file and move our options from Quick Start to it.

Node.js

You can also generate output programmatically by calling createClient() in a JavaScript/TypeScript file.

ts title="script.ts"
import { createClient } from '@hey-api/openapi-python';

createClient({
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
});

{/* ### Vite

If you're using Vite 5, 6, 7, or 8, you can integrate @hey-api/openapi-python directly into your build pipeline with @hey-api/vite-plugin. Install it alongside the main package:

Then add the plugin to your Vite configuration:

ts title="vite.config.ts"
import { heyApiPlugin } from '@hey-api/vite-plugin';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    heyApiPlugin({
      config: {
        input: 'hey-api/backend', // sign up at app.heyapi.dev
        output: 'src/client',
      },
    }),
  ],
});

See the Vite page for full configuration options. */}

Configuration

It's a good practice to extract your configuration into a separate file. Learn how to do that and discover available options on the Configuration page.

{/* */}


Web Src Content Docs Docs Openapi Python Migrating (`web/src/content/docs/docs/openapi/python/migrating.mdx`)


title: Migrating

import { Tabs, TabItem } from '@astrojs/starlight/components';

Migrating

Hey API is in active development, which allows us to move quickly and continuously improve the project. That pace sometimes comes with breaking changes. This page documents those changes and provides migration guidance for each release. If you need help with a migration, please open an issue.

v0.1.0

Coming soon.


Web Src Content Docs Docs Openapi Python Output (`web/src/content/docs/docs/openapi/python/output.mdx`)


title: Output

import { FileTree, Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';

Output

Every generated file in your output folder is created by a plugin. This page describes the default output, but similar logic applies to all plugins.

Overview

If you use the default configuration, your project might look like this.

- my-app/ - node_modules/ - src/ - client/ - client/ - core/ - client.gen.ts - index.ts - sdk.gen.ts - types.gen.ts - index.ts - package.json

Your actual output depends on your Hey API configuration. It may contain a different number of files and their contents might differ.

Let's go through each file in the src/client folder and explain what it looks like, what it does, and how to use it.

Client

client.gen.ts is generated by client plugins. If you choose to generate SDKs (enabled by default), we use the Fetch client unless specified otherwise.

ts title="client.gen.ts"
import { createClient, createConfig } from './client';

export const client = createClient(createConfig());

The contents of this file are consumed by SDKs, but you can also import client in your application to perform additional configuration or send manual requests.

Bundle

Client plugins provide their bundles inside client and core folders. The contents of these folders don't depend on the provided input. Everything inside these folders serves as a scaffolding so the generated code can make HTTP requests.

TypeScript

You can learn more on the TypeScript page.

SDK

You can learn more on the SDK page.

Entry File

index.ts is not generated by any specific plugin. It's meant for convenience and by default, it re-exports every artifact generated by default plugins (TypeScript and SDK).

ts title="index.ts"
export * from './sdk.gen';
export * from './types.gen';

Disable entry file

We recommend importing artifacts from their respective files to avoid ambiguity, but we leave this choice up to you.

ts title="index.ts"
import type { Pet } from './client';
// or
import type { Pet } from './client/types.gen';

If you're not importing artifacts from the entry file, you can skip generating it altogether by setting the output.entryFile option to false.

js {4} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: {
    entryFile: false,
    path: 'src/client',
  },
};

Re-export artifacts

You can choose which artifacts should be re-exported from the entry file using the includeInEntry option on any plugin. For example, we can re-export all Zod plugin artifacts by setting includeInEntry to true:

```ts title="index.ts" export { zAddPetData, zAddPetResponse, zApiResponse, zCategory, // ...more exports } from './zod.gen'; ``` ```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { includeInEntry: true, name: 'zod', }, ], }; ```

Or we can re-export only specific artifacts by providing a predicate function:

```ts title="index.ts" export { zTag } from './zod.gen'; ``` ```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { includeInEntry: (symbol) => symbol.name === 'zTag', name: 'zod', }, ], }; ```

Web Src Content Docs Docs Openapi Typescript Clients (`web/src/content/docs/docs/openapi/typescript/clients.mdx`)


title: Clients sidebar: label: Overview

import { Badge } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';

HTTP Clients

We all send HTTP requests in a slightly different way. Hey API doesn't force you to use any specific technology. What we do, however, is support your choice with great clients. All seamlessly integrated with our other features.

Features

  • seamless integration with @hey-api/openapi-ts ecosystem
  • type-safe response data and errors
  • response data validation and transformation
  • access to the original request and response
  • granular request and response customization options
  • minimal learning curve thanks to extending the underlying technology
  • support bundling inside the generated output

Plugins

Hey API natively supports the following clients.

Don't see your client? Build your own or let us know your interest by opening an issue.


Web Src Content Docs Docs Openapi Typescript Configuration (`web/src/content/docs/docs/openapi/typescript/configuration.mdx`)


title: Configuration sidebar: label: Overview

import { FileTree, Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import InputTabs from '@/components/code-groups/InputTabs.mdx';
import OutputTabs from '@/components/code-groups/OutputTabs.mdx';
import UserConfig from '@/components/UserConfig.astro';

Configuration

@hey-api/openapi-ts supports loading configuration from any file inside your project root folder supported by jiti loader. Below are the most common file formats.

```ts import { defineConfig } from '@hey-api/openapi-ts';

export default defineConfig({
input: 'hey-api/backend', // sign up at app.heyapi.dev
output: 'src/client',
});

text
</TabItem>
  <TabItem label="openapi-ts.config.cjs">
```js
/** @type {import('@hey-api/openapi-ts').UserConfig} */
module.exports = {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
};
```js /** @type {import('@hey-api/openapi-ts').UserConfig} */ export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', }; ```

Alternatively, you can use openapi-ts.config.js and configure the export statement depending on your project setup.

Input

You must provide an input so we can load your OpenAPI specification.

The input can be a string path, URL, API registry shorthand, an object containing any of these, or an object representing an OpenAPI specification. Hey API supports all valid OpenAPI versions and file formats.

You can learn more on the Input page.

:::tip
If you use an HTTPS URL with a self-signed certificate in development, you will need to set NODE_TLS_REJECT_UNAUTHORIZED=0 in your environment.
:::

Output

You must set the output so we know where to generate your files. It can be a path to the destination folder or an object containing the destination folder path and optional settings.

You can learn more on the Output page.

:::tip
You should treat the output folder as a dependency. Do not directly modify its contents as your changes might be erased when you run @hey-api/openapi-ts again.
:::

Parser

We parse your input before making it available to plugins. Configuring the parser is optional, but it provides an ideal opportunity to modify or validate your input as needed.

You can learn more on the Parser page.

Plugins

Plugins are responsible for generating artifacts from your input. By default, Hey API will generate TypeScript interfaces and SDK from your OpenAPI specification. You can add, remove, or customize any of the plugins. In fact, we highly encourage you to do so!

You can learn more on the Output page.

Advanced

More complex configuration scenarios can be handled by providing an array of inputs, outputs, or configurations.

Multiple jobs

Throughout this documentation, we generally reference single job configurations. However, you can easily run multiple jobs by providing an array of configuration objects.

```js export default [ { input: 'foo.yaml', output: 'src/foo', }, { input: 'bar.yaml', output: 'src/bar', }, ]; ``` - src - foo - client/ - core/ - client.gen.ts - index.ts - sdk.gen.ts - types.gen.ts - bar - client/ - core/ - client.gen.ts - index.ts - sdk.gen.ts - types.gen.ts

Job matrix

Reusing configuration across multiple jobs is possible by defining a job matrix. You can create a job matrix by providing input and output arrays of matching length.

```js export default { input: ['foo.yaml', 'bar.yaml'], output: ['src/foo', 'src/bar'], }; ``` - src - foo - client/ - core/ - client.gen.ts - index.ts - sdk.gen.ts - types.gen.ts - bar - client/ - core/ - client.gen.ts - index.ts - sdk.gen.ts - types.gen.ts

Merging inputs

You can merge inputs by defining multiple inputs and a single output.

```js export default { input: ['foo.yaml', 'bar.yaml'], output: 'src/client', }; ``` - src - client - client/ - core/ - client.gen.ts - index.ts - sdk.gen.ts - types.gen.ts

Web Src Content Docs Docs Openapi Typescript Core (`web/src/content/docs/docs/openapi/typescript/core.mdx`)


title: Core Plugins sidebar: label: Overview

import Examples from '@/components/Examples.astro';

Core

Apart from being responsible for the default output, core plugins are the foundation for other plugins. Instead of creating their own primitives, other plugins can reuse the artifacts from core plugins. This results in a smaller output size and a better user experience.

Plugins

Hey API provides the following core plugins.

Need another core plugin? Let us know by opening an issue.


Web Src Content Docs Docs Openapi Typescript Get Started (`web/src/content/docs/docs/openapi/typescript/get-started.mdx`)


title: Get Started

import InstallCommand from '@/components/package-managers/InstallCommand.astro';
import Examples from '@/components/Examples.astro';
import StackBlitz from '@/components/StackBlitz.astro';

Get Started

@hey-api/openapi-ts generates TypeScript code from OpenAPI specifications. Point it at your spec, pick your plugins, and get production-grade code in seconds.

Used by companies like Vercel, OpenCode, and PayPal.

“OpenAPI codegen that just works.”

— Guillermo Rauch, CEO of Vercel

Demo

<StackBlitz
example="hey-api-example"
openFile={[
'openapi-ts.config.ts',
'src/client/schemas.gen.ts',
'src/client/sdk.gen.ts',
'src/client/types.gen.ts',
]}
/>

Features

  • production-grade code that compiles
  • runs in any Node.js 22+ environment
  • accepts any OpenAPI specification
  • core plugins for SDKs, types, and schemas
  • HTTP clients for Fetch API, Angular, Axios, Next.js, Nuxt, and more
  • 20+ plugins to reduce third-party boilerplate
  • highly customizable via plugins
  • sync with Hey API Registry for spec management

Quick Start

The fastest way to use @hey-api/openapi-ts is via npx

sh
npx @hey-api/openapi-ts -i hey-api/backend -o src/client

Congratulations on creating your first client! 🎉 You can learn more about the generated files on the Output page.

Installation

You can download @hey-api/openapi-ts from npm using your favorite package manager.

Versioning

This package is in initial development. Please pin an exact version so you can safely upgrade when you're ready.

We publish migration notes for every breaking release. You might not be impacted by a breaking change if you don't use the affected features.

Usage

CLI

Most people run @hey-api/openapi-ts via CLI. To do that, add a script to your package.json file which will make openapi-ts executable through script.

json title="package.json"
"scripts": {
  "openapi-ts": "openapi-ts"
}

The above script can be executed by running npm run openapi-ts or equivalent command in other package managers. Next, we will create a configuration file and move our options from Quick Start to it.

Node.js

You can also generate output programmatically by calling createClient() in a JavaScript/TypeScript file.

ts title="script.ts"
import { createClient } from '@hey-api/openapi-ts';

createClient({
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
});

Vite

If you're using Vite 5, 6, 7, or 8, you can integrate @hey-api/openapi-ts directly into your build pipeline with @hey-api/vite-plugin. Install it alongside the main package:

Then add the plugin to your Vite configuration:

ts title="vite.config.ts"
import { heyApiPlugin } from '@hey-api/vite-plugin';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    heyApiPlugin({
      config: {
        input: 'hey-api/backend', // sign up at app.heyapi.dev
        output: 'src/client',
      },
    }),
  ],
});

See the Vite page for full configuration options.

Configuration

It's a good practice to extract your configuration into a separate file. Learn how to do that and discover available options on the Configuration page.


Web Src Content Docs Docs Openapi Typescript Integrations (`web/src/content/docs/docs/openapi/typescript/integrations.mdx`)


title: Hey API Platform sidebar: label: GitHub

import { Code, Tabs, TabItem } from '@astrojs/starlight/components';

Hey API Platform

:::caution
This feature is in development! 🎉 Try it out and provide feedback on GitHub.
:::

You can automate your client generation with Hey API Platform thanks to reproducible builds. Create dependency links between your clients and APIs, and watch the magic unfold. It's completely language and codegen agnostic.

Features

  • API version history
  • real-time updates
  • reproducible builds
  • language and codegen agnostic (TypeScript/Python/Go/Java/etc codegens are welcome)

Upload Specifications

Before you can generate clients, you must publish your OpenAPI specifications to Hey API.

Prerequisites

  1. Create a free account with Hey API.
  2. Create a new organization and project for your API provider. We recommend your naming matches your GitHub structure as it will be referenced by API clients. For example, we are using hey-api/backend for the platform.
  3. Inside your project, go to Integrations > APIs and generate an API key. Keep this value secret, it will be used to upload files.

Add GitHub CI workflow

Once you have your API key, you can start uploading OpenAPI specifications on every API build. We'll use our GitHub Action, but you can also make the API call manually if you're not using GitHub.

Create a new GitHub workflow or add an upload step to an existing workflow inside your API codebase. The example below will upload your OpenAPI specification to Hey API on every pull request and push to the main branch.

yaml title="github-workflow.yml"
name: Upload OpenAPI Specification

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  upload-openapi-spec:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

      - name: Upload OpenAPI spec
        uses: hey-api/[email protected]
        with:
          path-to-file: path/to/openapi.json
          tags: optional,custom,tags
        env:
          API_KEY: ${{ secrets.HEY_API_TOKEN }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Inputs

To successfully upload an OpenAPI specification, you need to provide the following inputs (see with in the example above)

`path-to-file`

A relative path to your OpenAPI file within the repository. Note that you might need an additional step in your GitHub workflow to generate this file (see FastAPI example).

`tags` (optional)

A comma-separated string value representing any custom tags you wish to add to your OpenAPI specification.

Environment Variables

In addition to the required path-to-file input, you must provide the following environment variables.

`API_KEY`

This is the project API key you obtained from Hey API.

:::caution
Personal API keys can't be used to upload specifications.
:::

`GITHUB_TOKEN`

This variable will be available inside your workflow by default. It's used to
fetch information about your repository, i.e., default branch.

Generate Clients

You can generate clients from public projects or any private projects you can access. The setup is largely the same, you want to configure the input path used by your codegen.

By default, we preserve the current behavior and return the latest specification. Let's have a closer look at the input path and change that.

Get API

As you can deduce from the examples above, the default command for fetching OpenAPI specifications looks like this.

text
https://get.heyapi.dev/<organization>/<project>

If you created an organization foo with project bar earlier, your URL would look like this.

text
https://get.heyapi.dev/foo/bar

Auth

Projects are private by default, you will need to be authenticated to download OpenAPI specifications. We recommend using project API keys in CI workflows and personal API keys for local development.

Once you have your API key, you can authenticate the request using the Authorization header or api_key query parameter.

text
https://get.heyapi.dev/foo/bar?api_key=<my_api_key>

Congratulations on fetching your first OpenAPI specification! 🎉

Filters

The default behavior returns the last uploaded specification. This might not be what you want. You can use a range of filters to narrow down the possible specifications, or pin your builds to an exact version.

`branch`

You can fetch the last build from branch by providing the branch query parameter.

text
https://get.heyapi.dev/foo/bar?branch=production

`commit_sha`

You can fetch an exact specification by providing the commit_sha query parameter. This will always return the same file.

text
https://get.heyapi.dev/foo/bar?commit_sha=0eb34c2024841ce95620f3ec02a2fea164ea4e9d

`tags`

If you're tagging your specifications with custom tags, you can use them to filter the results. When you provide multiple tags, only the first match will be returned.

text
https://get.heyapi.dev/foo/bar?tags=optional,custom,tags

`version`

Every OpenAPI document contains a required version field. You can use this value to fetch the last uploaded specification matching the value.

text
https://get.heyapi.dev/foo/bar?version=1.0.0

Feedback

We'd love your feedback! You can contact us on social media (search Hey API), email, or GitHub.

Pricing

The platform is currently in beta with our focus being on delivering a great experience. We plan to announce pricing once we have gathered enough data around usage patterns. However, we can guarantee there will always be a free plan available. Our mission to bring the finest tooling for working with APIs remains unchanged.


Web Src Content Docs Docs Openapi Typescript License (`web/src/content/docs/docs/openapi/typescript/license.md`)


title: License

License

MIT License

Copyright (c) 2026-present Hey API

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


Web Src Content Docs Docs Openapi Typescript Migrating (`web/src/content/docs/docs/openapi/typescript/migrating.mdx`)


title: Migrating

import { Tabs, TabItem } from '@astrojs/starlight/components';

Migrating

Hey API is in active development, which allows us to move quickly and continuously improve the project. That pace sometimes comes with breaking changes. This page documents those changes and provides migration guidance for each release. If you need help with a migration, please open an issue.

v0.99.0

Plugin duplicates behavior

If you specified the same plugin multiple times, only the last instance would be used. We eventually added a warning for this behavior, but that didn't treat the root issue. This release changes that by merging duplicate plugin configurations.

Renamed `plugin.symbols` to `plugin.imports`

To better represent its functionality, the plugin.symbols property has been renamed to plugin.imports.

Removed `plugin.external()` function

This function was used to reference external symbols. All plugins now use the Imports API, which allows you to reference external symbols in a type-safe way through plugin.imports.

Removed `plugin.registerSymbol()` function

This function was an alias for plugin.symbol() accepting a single argument. It has been removed in favor of plugin.symbol() which can now also accept a single argument.

v0.98.0

Declarative configuration

This is an internal change that simplifies the configuration and plugin APIs. The generated output should be unaffected, please open an issue if that's not the case. If you have custom plugins, refer to the custom plugin guide for the latest instructions.

v0.97.0

Changed `runtimeConfigPath` behavior

This was a known, long-standing issue confusing first-time users. Before, defining client runtimeConfigPath value would paste it verbatim to the generated output. This release changes the behavior to resolve relative to the output folder.

Changed Ky client behavior

The Ky client was updated to be more intuitive. Some Ky options now need to be passed via the kyOptions field and you need to pass undefined to unset an option.

Changed error interceptors behavior

Error interceptors now receive the result of the previous error interceptor. This aligns their behavior with request and response interceptors. This change only affects you if you use multiple interceptors.

Optional client request and response

The returned request and response objects are now typed as optional. This aligns the types with the actual runtime behavior, which remains unchanged.

Respect `throwOnError` option

Previously, there were instances where setting throwOnError to false would still throw an error. This most commonly happened when request validation failed. With this change, throwOnError is now truly respected.

v0.96.0

Removed Node 20 support

This release bumps the minimum required Node version to 22.13.

v0.95.0

Validator request schemas

Valibot and Zod plugins no longer export composite request Data schemas. Instead, each layer is exported as a separate schema. If you're using validators with SDKs, you can preserve the composite schema with shouldExtract:

```js {12-14} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'sdk', validator: 'zod', }, { name: 'zod', requests: { shouldExtract: true, }, }, ], }; ``` ```js {12-14} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'sdk', validator: 'valibot', }, { name: 'valibot', requests: { shouldExtract: true, }, }, ], }; ```

Removed `plugin.getSymbol()` function

This function has been removed. You can use plugin.querySymbol() instead. It accepts the same arguments and returns the same result.

v0.93.0

Removed resolver node

Valibot and Zod plugins no longer expose the enum.nodes.nullable node. Both plugins were refactored so that nullable values are handled outside of resolvers.

v0.92.0

Updated Symbol interface

The exportFrom property has been replaced with the getExportFromFilePath() function. This allows you to dynamically determine export paths based on symbol properties. This is a low-level feature, so you're most likely unaffected.

v0.91.0

Removed CommonJS (CJS) support

@hey-api/openapi-ts is now ESM-only. This change simplifies the codebase, improves tree-shaking, and enables better integration with modern bundlers and TypeScript tooling.

CommonJS entry points (require(), module.exports) are no longer supported. If you are in a CJS environment, you can still load the package dynamically using import() like:

js
const { defineConfig } = await import('@hey-api/openapi-ts');

If you have previously written:

js
const { defineConfig } = require('@hey-api/openapi-ts');

Migrate by updating your static imports:

js
import { defineConfig } from '@hey-api/openapi-ts';

If your environment cannot use ESM, pin to a previous version.

v0.90.0

Resolvers API

The Resolvers API has been simplified and expanded to provide a more consistent behavior across plugins. You can view a few common examples on the Resolvers page.

Structure API

The SDK plugin and Angular plugin now implement the Structure API, enabling more complex structures and fixing several known issues.

Some Structure APIs are incompatible with the previous configuration, most notably the methodNameBuilder function, which accepted the operation object as an argument. You can read the SDK Output section to familiarize yourself with the Structure API.

Please open an issue if you're unable to migrate your configuration to the new syntax.

v0.89.0

Prefer named exports

This release changes the default for index.ts to prefer named exports. Named exports may lead to better IDE and bundler performance compared to asterisk (*) as your tooling doesn't have to inspect the underlying module to discover exports.

While this change is merely cosmetic, you can set output.preferExportAll to true if you prefer to use the asterisk.

js {5} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: {
    path: 'src/client',
    preferExportAll: true,
  },
};

Removed `symbol:setValue:*` events

These events have been removed in favor of node:set:* events.

v0.88.0

Removed `compiler` and `tsc` exports

This release removes the compiler utility functions. Instead, it introduces a new TypeScript DSL exposed under the $ symbol. All plugins now use this interface, so you may notice slight changes in the generated output.

v0.87.0

Removed legacy clients

This release removes support for legacy clients and plugins. Please migrate to the new clients if you haven't done so yet. If you're unable to do so due to a missing feature, let us know on GitHub.

v0.86.0

Removed Node 18 support

This release bumps the minimum required Node version to 20.19.

v0.85.0

Updated `output` options

We made the output configuration more consistent by using null to represent disabled options. This change does not affect boolean options.

js ins={5,7,10} del={4,6,9} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: {
    format: false,
    format: null,
    lint: false,
    lint: null,
    path: 'src/client',
    tsConfigPath: 'off',
    tsConfigPath: null,
  },
};

Updated Pinia Colada query options

Pinia Colada query options now use defineQueryOptions to improve reactivity support. Instead of calling the query options function, you can use one of the following approaches.

```ts title="colada.gen.ts" useQuery(getPetsQuery); ``` ```ts title="colada.gen.ts" useQuery(getPetByIdQuery, () => ({ path: { petId: 1, }, })); ``` ```ts title="colada.gen.ts" const petId = ref(1);

useQuery(getPetByIdQuery, () => ({
path: {
petId: petId.value,
},
}));

text
</TabItem>
  <TabItem label="properties">
```ts title="colada.gen.ts"
const petId = ref<number | null>(1);

useQuery(() => ({
  ...getPetByIdQuery({
    path: { petId: petId.value as number },
  }),
  enabled: () => petId.value !== null,
}));

v0.84.0

Symbol API

This release improves the Symbol API, which adds the capability to place symbols in arbitrary files. We preserved the previous output structure for all plugins except Angular.

You can preserve the previous Angular output by writing your own placement function.

TypeScript renderer

We ship a dedicated TypeScript renderer for .ts files. This release improves the renderer's ability to group and sort imported modules, resulting in a more polished output.

Removed `output` plugin option

Due to the Symbol API release, this option has been removed from the Plugin API.

v0.83.0

Symbol API

This release adds the Symbol API, which significantly reduces the risk of naming collisions. While the generated output should only include formatting changes, this feature introduces breaking changes to the Plugin API that affect custom plugins.

We will update the custom plugin guide once the Plugin API becomes more stable.

Removed `groupByTag` Pinia Colada option

This option has been removed to provide a more consistent API across plugins. We plan to bring it back in a future release.

v0.82.0

Hooks API

This release adds the Hooks API, giving you granular control over which operations generate queries and mutations. As a result, we tightened the previous behavior and POST operations no longer generate queries by default. To preserve the old behavior, add a custom matcher.

js {7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  parser: {
    hooks: {
      operations: {
        isQuery: (op) => (op.method === 'post' ? true : undefined),
      },
    },
  },
};

v0.81.0

Server-Sent Events (SSE)

This release adds support for server-sent events (SSE). Instead of treating text/event-stream content types as regular HTTP methods, we now generate SSE streams. In practice, you will want to update your affected endpoints to process streamed events.

```js title="client.gen.ts" const { data } = await foo(); console.log(data.type); ``` ```js title="client.gen.ts" const { stream } = await foo(); for await (const event of stream) { console.log(event.type); } ```

v0.80.0

Added Zod 4 and Zod Mini

This release adds support for Zod 4 and Zod Mini. By default, the zod plugin will generate output for Zod 4. If you want to preserve the previous output for Zod 3 or use Zod Mini, set compatibilityVersion to 3 or mini.

```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 3, }, ], }; ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 'mini', }, ], }; ```

v0.79.0

Removed `typescript+namespace` enums mode

Due to a simpler TypeScript plugin implementation, the typescript+namespace enums mode is no longer necessary. This mode was used in the past to group inline enums under the same namespace. With the latest changes, this behavior is no longer supported. You can either choose to ignore inline enums (default), or use the enums transform (added in v0.78.0) to convert them into reusable components which will get exported as usual.

v0.78.0

Added `parser` options

Previously, @hey-api/typescript would generate correct types, but the validator plugins would have to re-implement the same logic or generate schemas that didn't match the generated types.

Since neither option was ideal, this release adds a dedicated place for parser options. Parser is responsible for preparing the input so plugins can generate more accurate output with less effort.

You can learn more about configuring parser on the Parser page.

Moved `input` options

The following options were moved to the new parser group.

  • input.filters moved to parser.filters
  • input.pagination moved to parser.pagination
  • input.patch moved to parser.patch
  • input.validate_EXPERIMENTAL moved to parser.validate_EXPERIMENTAL

Updated `typescript` options

The following options were renamed.

  • enumsCase moved to enums.case
  • enumsConstantsIgnoreNull moved to enums.constantsIgnoreNull

Moved `typescript` options

The following options were moved to the new parser group.

  • exportInlineEnums moved to parser.transforms.enums
  • readOnlyWriteOnlyBehavior moved to parser.transforms.readWrite.enabled
  • readableNameBuilder moved to parser.transforms.readWrite.responses.name
  • writableNameBuilder moved to parser.transforms.readWrite.requests.name

Updated `readWrite.responses` name

Additionally, the naming pattern for response schemas has changed from {name}Readable to {name}. This is to prevent your code from breaking by default when using a schema that gets updated with a write-only field.

v0.77.0

Updated `sdk.validator` option

Clients can now validate both request and response data. As a result, passing a boolean or string to validator will control both of these options. To preserve the previous behavior, set validator.request to false and validator.response to your previous configuration.

js ins={9-12} del={8} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@hey-api/sdk',
      validator: true,
      validator: {
        request: false,
        response: true,
      },
    },
  ],
};

Updated Plugin API

Please refer to the custom plugin tutorial for the latest guide.

v0.76.0

Single Valibot schema per request

Previously, we generated a separate schema for each endpoint parameter and request body. In v0.76.0, a single request schema is generated for the whole endpoint. It may contain a request body, parameters, and headers.

ts
const vData = v.object({
  body: v.optional(
    v.object({
      foo: v.optional(v.string()),
      bar: v.optional(v.union([v.number(), v.null()])),
    }),
  ),
  headers: v.optional(v.never()),
  path: v.object({
    baz: v.string(),
  }),
  query: v.optional(v.never()),
});

If you need to access individual fields, you can do so using the .entries API. For example, we can get the request body schema with vData.entries.body.

v0.75.0

Updated TanStack Query options

The TanStack Query plugin options have been expanded to support more naming and casing patterns. As a result, the following options have been renamed.

  • queryOptionsNameBuilder renamed to queryOptions
  • infiniteQueryOptionsNameBuilder renamed to infiniteQueryOptions
  • mutationOptionsNameBuilder renamed to mutationOptions
  • queryKeyNameBuilder renamed to queryKeys
  • infiniteQueryKeyNameBuilder renamed to infiniteQueryKeys

Added `plugin.forEach()` method

This method replaces the .subscribe() method. Additionally, .forEach() is executed immediately, which means we don't need the before and after events – simply move your code before and after the .forEach() block.

ts del={1-4} ins={5}
plugin.subscribe('operation', (event) => {
  // do something with event
});
plugin.subscribe('schema', (event) => {
plugin.forEach('operation', 'schema', (event) => {
  // do something with event
});

v0.74.0

Single Zod schema per request

Previously, we generated a separate schema for each endpoint parameter and request body. In v0.74.0, a single request schema is generated for the whole endpoint. It may contain a request body, parameters, and headers.

ts
const zData = z.object({
  body: z
    .object({
      foo: z.string().optional(),
      bar: z.union([z.number(), z.null()]).optional(),
    })
    .optional(),
  headers: z.never().optional(),
  path: z.object({
    baz: z.string(),
  }),
  query: z.never().optional(),
});

If you need to access individual fields, you can do so using the .shape API. For example, we can get the request body schema with zData.shape.body.

v0.73.0

Bundle `@hey-api/client-*` plugins

In previous releases, you had to install a separate client package to generate a fully working output, e.g., npm install @hey-api/client-fetch. This created a few challenges: getting started was slower, upgrading was sometimes painful, and bundling too. Beginning with v0.73.0, all Hey API clients are bundled by default and don't require installing any additional dependencies. You can remove any installed client packages and re-run @hey-api/openapi-ts.

sh
npm uninstall @hey-api/client-fetch

v0.72.0

Added `sdk.classStructure` option

When generating class-based SDKs, we now try to infer the ideal structure using operationId keywords. If you'd like to preserve the previous behavior, set classStructure to off.

js {7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      classStructure: 'off',
      name: '@hey-api/sdk',
    },
  ],
};

v0.71.0

Renamed `sdk.serviceNameBuilder` option

This option has been renamed to sdk.classNameBuilder to better represent its functionality. Additionally, it's no longer set by default. To preserve the previous behavior, update your configuration.

js ins={7} del={9} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      classNameBuilder: '{{name}}Service',
      name: '@hey-api/sdk',
      serviceNameBuilder: '{{name}}Service',
    },
  ],
};

v0.68.0

Upgraded input filters

Input filters now avoid generating invalid output without requiring you to specify every missing schema as in the previous releases. As part of this release, we changed the way filters are configured and removed the support for regular expressions. Let us know if regular expressions are still useful for you and want to bring them back!

```js {6,9} del={12} title="openapi-ts.config.ts" export default { input: { // match only the schema named `foo` and `GET` operation for the `/api/v1/foo` path filters: { operations: { include: ['GET /api/v1/foo'], }, schemas: { include: ['foo'], }, }, include: '^(#/components/schemas/foo|#/paths/api/v1/foo/get)$', path: 'hey-api/backend', // sign up at app.heyapi.dev }, output: 'src/client', plugins: ['@hey-api/client-fetch'], }; ``` ```js {7,10} del={4} title="openapi-ts.config.ts" export default { input: { // match everything except for the schema named `foo` and `GET` operation for the `/api/v1/foo` path exclude: '^(#/components/schemas/foo|#/paths/api/v1/foo/get)$', filters: { operations: { exclude: ['GET /api/v1/foo'], }, schemas: { exclude: ['foo'], }, }, path: 'hey-api/backend', // sign up at app.heyapi.dev }, output: 'src/client', plugins: ['@hey-api/client-fetch'], }; ```

v0.67.0

Respecting `moduleResolution` value in `tsconfig.json`

This release introduces functionality related to your tsconfig.json file. The initial feature properly respects the value of your moduleResolution field. If you're using nodenext, the relative module paths in your output will be appended with .js. To preserve the previous behavior where we never appended .js to relative module paths, set output.tsConfigPath to off.

js {5} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: {
    path: 'src/client',
    tsConfigPath: 'off',
  },
};

v0.66.0

Read-only and write-only fields

Starting with v0.66.0, @hey-api/typescript will generate separate types for payloads and responses if it detects any read-only or write-only fields. To preserve the previous behavior and generate a single type regardless, set readOnlyWriteOnlyBehavior to off.

js {8} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@hey-api/typescript',
      readOnlyWriteOnlyBehavior: 'off',
    },
  ],
};

v0.64.0

Added `ClientOptions` interface

The Config interface now accepts an optional generic extending ClientOptions instead of boolean type ThrowOnError.

ts del={1} ins={2}
type Foo = Config<false>;
type Foo = Config<{ throwOnError: false }>;

Added `client.baseUrl` option

You can use this option to configure the default base URL for the generated client. By default, we will attempt to resolve the first defined server or infer the base URL from the input path. If you'd like to preserve the previous behavior, set baseUrl to false.

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    {
      baseUrl: false,
      name: '@hey-api/client-fetch',
    },
  ],
};

v0.63.0

Client plugins

Clients are now plugins generating their own client.gen.ts file. There's no migration needed if you're using CLI. If you're using the configuration file, move client options to plugins.

js del={2} ins={5} title="openapi-ts.config.ts"
export default {
  client: '@hey-api/client-fetch',
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: ['@hey-api/client-fetch'],
};

Added `client.gen.ts` file

Related to above, the internal client instance previously located in sdk.gen.ts is now defined in client.gen.ts. If you're importing it in your code, update the import module.

js del={1} ins={2}
import { client } from 'client/sdk.gen';
import { client } from 'client/client.gen';

Moved `sdk.throwOnError` option

This SDK configuration option has been moved to the client plugins where applicable. Not every client can be configured to throw on error, so it didn't make sense to expose the option when it didn't have any effect.

js ins={7} del={11} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    {
      name: '@hey-api/client-fetch',
      throwOnError: true,
    },
    {
      name: '@hey-api/sdk',
      throwOnError: true,
    },
  ],
};

v0.62.0

Changed parser

Formerly known as the experimental parser, this is now the default parser. This change should not impact the generated output's functionality. However, there might be cases where this results in breaking changes due to different handling of certain scenarios. If you need to revert to the legacy parser, set the experimentalParser flag to false.

js {3} title="openapi-ts.config.ts"
export default {
  client: '@hey-api/client-fetch',
  experimentalParser: false,
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
};

Note that the legacy parser is no longer supported and will be removed in the v1 release.

v0.61.0

Added `auth` option

Client package functions accessToken and apiKey were replaced with a single auth function for fetching auth tokens. If your API supports multiple auth mechanisms, you can use the auth argument to return the appropriate token.

js del={4-5} ins={6}
import { client } from 'client/sdk.gen';

client.setConfig({
  accessToken: () => '<my_token>',
  apiKey: () => '<my_token>',
  auth: (auth) => '<my_token>',
});

Due to conflict with the Axios native auth option, we removed support for configuring Axios auth. Please let us know if you require this feature added back.

Added `watch` option

While this is a new feature, supporting it involved replacing the @apidevtools/json-schema-ref-parser dependency with our own implementation. Since this was a big change, we're applying caution and marking this as a breaking change.

Changed `parseAs: 'auto'` behavior

The Fetch API client will return raw response body as ReadableStream when Content-Type response header is undefined and parseAs is auto.

v0.60.0

Added `sdk.transformer` option

When generating SDKs, you now have to specify transformer in order to modify response data. By default, adding @hey-api/transformers to your plugins will only produce additional output. To preserve the previous functionality, set sdk.transformer to true.

js {13} title="openapi-ts.config.ts"
export default {
  client: '@hey-api/client-fetch',
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      dates: true,
      name: '@hey-api/transformers',
    },
    {
      name: '@hey-api/sdk',
      transformer: true,
    },
  ],
};

v0.59.0

Added `logs.level` option

You can now configure different log levels. As part of this feature, we had to introduce a breaking change by moving the debug option to logs.level. This will affect you if you're calling @hey-api/openapi-ts from Node.js (not CLI) or using the configuration file.

js del={3} ins={6} title="openapi-ts.config.ts"
export default {
  client: '@hey-api/client-fetch',
  debug: true,
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  logs: {
    level: 'debug',
  },
  output: 'src/client',
};

Updated default `plugins`

@hey-api/schemas has been removed from the default plugins. To continue using it, add it to your plugins array.

js {8} title="openapi-ts.config.ts"
export default {
  client: '@hey-api/client-fetch',
  experimentalParser: true,
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    '@hey-api/schemas',
  ],
};

v0.58.0

Removed `schemas.gen.ts` re-export

index.ts will no longer re-export schemas.gen.ts to reduce the chance of producing broken output. Please update your code to import from schemas.gen.ts directly.

js del={1} ins={2}
import { mySchema } from 'client';
import { mySchema } from 'client/schemas.gen';

Removed `transformers.gen.ts` re-export

index.ts will no longer re-export transformers.gen.ts to reduce the chance of producing broken output. Please update your code to import from transformers.gen.ts directly.

js del={1} ins={2}
import { myTransformer } from 'client';
import { myTransformer } from 'client/transformers.gen';

Added `output.clean` option

By default, the output.path folder will be emptied on every run. To preserve the previous behavior, set output.clean to false.

js {5} title="openapi-ts.config.ts"
export default {
  client: '@hey-api/client-fetch',
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: {
    clean: false,
    path: 'src/client',
  },
};

Added `typescript.identifierCase` option

This change affects only the experimental parser. By default, the generated TypeScript interfaces will follow the PascalCase naming convention. In the previous versions, we tried to preserve the original name as much as possible. To keep the previous behavior, set typescript.identifierCase to preserve.

js {9} title="openapi-ts.config.ts"
export default {
  client: '@hey-api/client-fetch',
  experimentalParser: true,
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      identifierCase: 'preserve',
      name: '@hey-api/typescript',
    },
  ],
};

v0.57.0

Renamed `@hey-api/services` plugin

This plugin has been renamed to @hey-api/sdk.

Changed `sdk.output` value

To align with the updated name, the @hey-api/sdk plugin will generate an sdk.gen.ts file. This will result in a breaking change if you're importing from services.gen.ts. Please update your imports to reflect this change.

js del={1} ins={2}
import { client } from 'client/services.gen';
import { client } from 'client/sdk.gen';

Renamed `@hey-api/types` plugin

This plugin has been renamed to @hey-api/typescript.

Added `typescript.exportInlineEnums` option

By default, inline enums (enums not defined as reusable components in the input file) will be generated only as inlined union types. You can set exportInlineEnums to true to treat inline enums as reusable components. When true, the exported enums will follow the style defined in enums.

This is a breaking change since in the previous versions, inline enums were always treated as reusable components. To preserve your current output, set exportInlineEnums to true. This feature works only with the experimental parser.

js {9} title="openapi-ts.config.ts"
export default {
  client: '@hey-api/client-fetch',
  experimentalParser: true,
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      exportInlineEnums: true,
      name: '@hey-api/typescript',
    },
  ],
};

v0.56.0

Deprecated `tree` in `@hey-api/types`

This config option is deprecated and will be removed when the experimental parser becomes the default.

v0.55.0

This release adds the ability to filter your OpenAPI specification before it's processed. This feature will be useful if you are working with a large specification and are interested in generating output only from a small subset.

This feature is available only in the experimental parser. In the future, this will become the default parser. To opt-in to the experimental parser, set the experimentalParser flag in your configuration to true.

Deprecated `include` in `@hey-api/types`

This config option is deprecated and will be removed when the experimental parser becomes the default.

Deprecated `filter` in `@hey-api/services`

This config option is deprecated and will be removed when the experimental parser becomes the default.

Added `input.include` option

This config option can be used to replace the deprecated options. It accepts a regular expression string matching against references within the bundled specification.

js {5} title="openapi-ts.config.ts"
export default {
  client: '@hey-api/client-fetch',
  experimentalParser: true,
  input: {
    include: '^(#/components/schemas/foo|#/paths/api/v1/foo/get)$',
    path: 'hey-api/backend', // sign up at app.heyapi.dev
  },
  output: 'src/client',
};

The configuration above will process only the schema named foo and GET operation for the /api/v1/foo path.

v0.54.0

This release makes plugins first-class citizens. In order to achieve that, the following breaking changes were introduced.

Removed CLI options

The --types, --schemas, and --services CLI options have been removed. You can list which plugins you'd like to use explicitly by passing a list of plugins as --plugins

Removed `*.export` option

Previously, you could explicitly disable export of certain artifacts using the *.export option or its shorthand variant. These were both removed. You can now disable export of specific artifacts by manually defining an array of plugins and excluding the unwanted plugin.

```js ins={6} del={5} title="openapi-ts.config.ts" export default { client: '@hey-api/client-fetch', input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', schemas: false, plugins: ['@hey-api/types', '@hey-api/services'], }; ``` ```js ins={8} del={6} title="*.export" export default { client: '@hey-api/client-fetch', input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', schemas: { export: false, }, plugins: ['@hey-api/types', '@hey-api/services'], }; ```

Renamed `schemas.name` option

Each plugin definition contains a name field. This was conflicting with the schemas.name option. As a result, it has been renamed to nameBuilder.

js del={6} ins={11} title="openapi-ts.config.ts"
export default {
  client: '@hey-api/client-fetch',
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  schemas: {
    name: (name) => `${name}Schema`,
  },
  plugins: [
    // ...other plugins
    {
      nameBuilder: (name) => `${name}Schema`,
      name: '@hey-api/schemas',
    },
  ],
};

Removed `services.include` shorthand option

Previously, you could use a string value as a shorthand for the services.include configuration option. You can now achieve the same result using the include option.

js del={5} ins={9} title="openapi-ts.config.ts"
export default {
  client: '@hey-api/client-fetch',
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  services: '^MySchema',
  plugins: [
    // ...other plugins
    {
      include: '^MySchema',
      name: '@hey-api/services',
    },
  ],
};

Renamed `services.name` option

Each plugin definition contains a name field. This was conflicting with the services.name option. As a result, it has been renamed to serviceNameBuilder.

js del={6} ins={11} title="openapi-ts.config.ts"
export default {
  client: '@hey-api/client-fetch',
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  services: {
    name: '{{name}}Service',
  },
  plugins: [
    // ...other plugins
    {
      serviceNameBuilder: '{{name}}Service',
      name: '@hey-api/services',
    },
  ],
};

Renamed `types.dates` option

Previously, you could set types.dates to a boolean or a string value, depending on whether you wanted to transform only type strings into dates, or runtime code too. Many people found these options confusing, so they have been simplified to a boolean and extracted into a separate @hey-api/transformers plugin.

js del={6} ins={11} title="openapi-ts.config.ts"
export default {
  client: '@hey-api/client-fetch',
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  types: {
    dates: 'types+transform',
  },
  plugins: [
    // ...other plugins
    {
      dates: true,
      name: '@hey-api/transformers',
    },
  ],
};

Removed `types.include` shorthand option

Previously, you could use a string value as a shorthand for the types.include configuration option. You can now achieve the same result using the include option.

js del={5} ins={9} title="openapi-ts.config.ts"
export default {
  client: '@hey-api/client-fetch',
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  types: '^MySchema',
  plugins: [
    // ...other plugins
    {
      include: '^MySchema',
      name: '@hey-api/types',
    },
  ],
};

Renamed `types.name` option

Each plugin definition contains a name field. This was conflicting with the types.name option. As a result, it has been renamed to style.

js del={6} ins={12} title="openapi-ts.config.ts"
export default {
  client: '@hey-api/client-fetch',
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  types: {
    name: 'PascalCase',
  },
  plugins: [
    // ...other plugins
    {
      name: '@hey-api/types',
      style: 'PascalCase',
    },
  ],
};

v0.53.0

Changed schemas name pattern

Previously, generated schemas would have their definition names prefixed with $. This was problematic when using them with Svelte due to reserved keyword conflicts. The new naming pattern for schemas suffixes their definition names with Schema. You can continue using the previous pattern by setting the schemas.name configuration option.

js {5} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  schemas: {
    name: (name) => `$${name}`,
  },
};

Renamed legacy clients

Legacy clients were renamed to signal they are deprecated more clearly. To continue using legacy clients, you will need to update your configuration and prefix them with legacy/.

```js ins={3} del={2} title="openapi-ts.config.ts" export default { client: 'fetch', client: 'legacy/fetch', input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', }; ``` ```js ins={3} del={2} title="openapi-ts.config.ts" export default { client: 'axios', client: 'legacy/axios', input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', }; ``` ```js ins={3} del={2} title="openapi-ts.config.ts" export default { client: 'angular', client: 'legacy/angular', input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', }; ``` ```js ins={3} del={2} title="openapi-ts.config.ts" export default { client: 'node', client: 'legacy/node', input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', }; ``` ```js ins={3} del={2} title="openapi-ts.config.ts" export default { client: 'xhr', client: 'legacy/xhr', input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', }; ```

v0.52.0

Removed internal `client` export

Previously, client packages would create a default client which you'd then import and configure.

js
import { client, createClient } from '@hey-api/client-fetch';

createClient({
  baseUrl: 'https://example.com',
});

console.log(client.getConfig().baseUrl); // <-- 'https://example.com'

This client instance was used internally by services unless overridden. Apart from running createClient() twice, people were confused about the meaning of global configuration option.

Starting with v0.52.0, client packages will not create a default client. Instead, services will define their own client. You can now achieve the same configuration by importing client from services and using the new setConfig() method.

js
import { client } from 'client/services.gen';

client.setConfig({
  baseUrl: 'https://example.com',
});

console.log(client.getConfig().baseUrl); // <-- 'https://example.com'

v0.51.0

Required `client` option

Client now has to be explicitly specified and @hey-api/openapi-ts will no longer generate a legacy Fetch API client by default. To preserve the previous default behavior, set the client option to fetch.

js {2} title="openapi-ts.config.ts"
export default {
  client: 'fetch',
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
};

v0.48.0

Changed `methodNameBuilder()` signature

The services.methodNameBuilder() function now provides a single operation argument instead of multiple cherry-picked properties from it.

js del={7} ins={8} title="openapi-ts.config.ts"
import { createClient } from '@hey-api/openapi-ts';

createClient({
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  services: {
    methodNameBuilder: (service, name) => name,
    methodNameBuilder: (operation) => operation.name,
  },
});

v0.46.0

Tree-shakeable services

By default, your services will now support tree-shaking. You can either use wildcard imports

js del={1} ins={2}
import { DefaultService } from 'client/services.gen';
import * as DefaultService from 'client/services.gen';

DefaultService.foo(); // only import needs to be changed

or update all references to service classes

js del={1} ins={2}
import { DefaultService } from 'client/services.gen';
import { foo } from 'client/services.gen';

foo(); // all references need to be changed

If you want to preserve the old behavior, you can set the newly exposed services.asClass option to true.

js {5} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  services: {
    asClass: true,
  },
};

v0.45.0

Removed `client` inference

@hey-api/openapi-ts will no longer infer which client you want to generate. By default, we will create a fetch client. If you want a different client, you can specify it using the client option.

js {2} title="openapi-ts.config.ts"
export default {
  client: 'axios',
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
};

v0.44.0

Moved `format`

This config option has been moved. You can now configure formatter using the output.format option.

js del={2} ins={5} title="openapi-ts.config.ts"
export default {
  format: 'prettier',
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: {
    format: 'prettier',
    path: 'src/client',
  },
};

Moved `lint`

This config option has been moved. You can now configure linter using the output.lint option.

js del={3} ins={5} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  lint: 'eslint',
  output: {
    lint: 'eslint',
    path: 'src/client',
  },
};

v0.43.0

Removed `enums.gen.ts`

This file has been removed. Instead, enums are exported from types.gen.ts. If you use imports from enums.gen.ts, you should be able to easily find and replace all instances.

js del={1} ins={2}
import { Foo } from 'client/enums.gen';
import { Foo } from 'client/types.gen';

Removed `Enum` postfix

Generated enum names are no longer postfixed with Enum. You can either alias your imports

js del={1} ins={2}
import { FooEnum } from 'client/types.gen';
import { Foo as FooEnum } from 'client/types.gen';

console.log(FooEnum.value); // only import needs to be changed

or update all references to enums

js del={1} ins={2}
import { FooEnum } from 'client/types.gen';
import { Foo } from 'client/types.gen';

console.log(Foo.value); // all references need to be changed

Moved `enums`

This config option has been moved. You can now configure enums using the types.enums option.

js del={2} ins={6} title="openapi-ts.config.ts"
export default {
  enums: 'javascript',
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  types: {
    enums: 'javascript',
  },
};

v0.42.0

Changed `format`

This config option has changed. You now need to specify a value (biome or prettier) to format the output (default: false).

js {2}
export default {
  format: 'prettier',
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
}

Changed `lint`

This config option has changed. You now need to specify a value (biome or eslint) to lint the output (default: false).

js {3}
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  lint: 'eslint',
  output: 'src/client',
}

Moved `operationId`

This config option has been moved. You can now configure it using the services.operationId option.

js {5}
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  services: {
    operationId: true,
  },
}

v0.41.0

Removed `postfixServices`

This config option has been removed. You can now transform service names using the string pattern parameter.

js {5}
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  services: {
    name: 'myAwesome{{name}}Api',
  },
}

Removed `serviceResponse`

This config option has been removed. You can now configure service responses using the services.response option.

js {5}
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  services: {
    response: 'body',
  },
}

Removed `useDateType`

This config option has been removed. You can now configure date type using the types.dates option.

js {5}
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  type: {
    dates: true,
  },
}

v0.40.0

Renamed `models.gen.ts` file

models.gen.ts is now called types.gen.ts. If you use imports from models.gen.ts, you should be able to easily find and replace all instances.

js del={1} ins={2}
import type { Model } from 'client/models.gen';
import type { Model } from 'client/types.gen';

Renamed `exportModels`

This config option is now called types.

PascalCase for types

You can now choose to export types using the PascalCase naming convention.

js {5}
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  types: {
    name: 'PascalCase',
  },
}

Exported `enums.gen.ts` file

Enums are now re-exported from the main index.ts file.

v0.39.0

Single `enums.gen.ts` file

Enums are now exported from a separate file. If you use imports from models.ts, you can change them to enums.gen.ts.

js del={1} ins={2}
import { Enum } from 'client/models';
import { Enum } from 'client/enums.gen';

Renamed `models.ts` file

models.ts is now called models.gen.ts. If you use imports from models.ts, you should be able to easily find and replace all instances.

js del={1} ins={2}
import type { Model } from 'client/models';
import type { Model } from 'client/models.gen';

Renamed `schemas.ts` file

schemas.ts is now called schemas.gen.ts. If you use imports from schemas.ts, you should be able to easily find and replace all instances.

js del={1} ins={2}
import { $Schema } from 'client/schemas';
import { $Schema } from 'client/schemas.gen';

Renamed `services.ts` file

services.ts is now called services.gen.ts. If you use imports from services.ts, you should be able to easily find and replace all instances.

js del={1} ins={2}
import { DefaultService } from 'client/services';
import { DefaultService } from 'client/services.gen';

Deprecated exports from `index.ts`

Until this release, index.ts file exported all generated artifacts. Starting from this release, enums are no longer exported from index.ts. Models, schemas, and services will continue to be exported from index.ts to avoid a huge migration lift, but we recommend migrating to import groups per artifact type.

js del={1} ins={2-5}
import { Enum, type Model, $Schema, DefaultService } from 'client';
import { Enum } from 'client/enums.gen';
import type { Model } from 'client/models.gen';
import { $Schema } from 'client/schemas.gen';
import { DefaultService } from 'client/services.gen';

Prefer `unknown`

Types that cannot be determined will now be generated as unknown instead of any. To dismiss any errors, you can cast your variables back to any, but we recommend updating your code to work with unknown types.

js
const foo = bar as any

v0.38.0

Renamed `write`

This config option is now called dryRun (file) or --dry-run (CLI). To restore existing functionality, invert the value, ie. write: true is dryRun: false and write: false is dryRun: true.

v0.36.0

JSON Schema 2020-12

Schemas are exported directly from OpenAPI specification. This means your schemas might change depending on which OpenAPI version you're using. If this release caused a field to be removed, consult the JSON Schema documentation on how to obtain the same value from JSON Schema (e.g., required properties).

Renamed `exportSchemas`

This config option is now called schemas.

v0.35.0

Removed `postfixModels`

This config option has been removed.

v0.34.0

Single `services.ts` file

Services are now exported from a single file. If you used imports from individual service files, these will need to be updated to refer to the single services.ts file.

v0.31.1

Merged enums options

useLegacyEnums config option is now enums: 'typescript' and existing enums: true option is now enums: 'javascript'.

v0.31.0

Single `models.ts` file

TypeScript interfaces are now exported from a single file. If you used imports from individual model files, these will need to be updated to refer to the single models.ts file.

Single `schemas.ts` file

Schemas are now exported from a single file. If you used imports from individual schema files, these will need to be updated to refer to the single schemas.ts file.

v0.27.38

`useOptions: true`

By default, generated clients will use a single object argument to pass values to API calls. This is a significant change from the previous default of unspecified array of arguments. If migrating your application in one go isn't feasible, we recommend deprecating your old client and generating a new client.

ts
import { DefaultService } from 'client/services'; // <-- old client with array arguments

import { DefaultService } from 'client_v2/services'; // <-- new client with options argument

This way, you can gradually switch over to the new syntax as you update parts of your code. Once you've removed all instances of client imports, you can safely delete the old client folder and find and replace all client_v2 calls to client.

v0.27.36

`exportSchemas: true`

By default, we will create schemas from your OpenAPI specification. Use exportSchemas: false to preserve the old behavior.

v0.27.32

Renamed `Config` interface

This interface is now called UserConfig.

v0.27.29

Renamed `openapi` CLI command

This command is now called openapi-ts.

v0.27.26

Removed `indent`

This config option has been removed. Use a code formatter to modify the generated files code style according to your preferences.

v0.27.24

Removed `useUnionTypes`

This config option has been removed. Generated types will behave the same as useUnionTypes: true before.

OpenAPI TypeScript Codegen

@hey-api/openapi-ts was originally forked from Ferdi Koomen's openapi-typescript-codegen. Therefore, we want you to be able to migrate your projects. Migration should be relatively straightforward if you follow the release notes on this page. Start on v0.27.24 and scroll to the release you're migrating to.


Web Src Content Docs Docs Openapi Typescript Mocks (`web/src/content/docs/docs/openapi/typescript/mocks.mdx`)


title: Testing sidebar: label: Overview

import { Badge } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';

Testing

Testing is an important component of every robust development process. Generate realistic mock data, intercept requests at the network layer, or drive assertions against a running server, all from your specification.

Plugins

Hey API natively supports the following testing libraries.

Don't see your library? Let us know by opening an issue.


Web Src Content Docs Docs Openapi Typescript Output (`web/src/content/docs/docs/openapi/typescript/output.mdx`)


title: Output

import { FileTree, Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';

Output

Every generated file in your output folder is created by a plugin. This page describes the default output, but similar logic applies to all plugins.

Overview

If you use the default configuration, your project might look like this.

- my-app/ - node_modules/ - src/ - client/ - client/ - core/ - client.gen.ts - index.ts - sdk.gen.ts - types.gen.ts - index.ts - package.json

Your actual output depends on your Hey API configuration. It may contain a different number of files and their contents might differ.

Let's go through each file in the src/client folder and explain what it looks like, what it does, and how to use it.

Client

client.gen.ts is generated by client plugins. If you choose to generate SDKs (enabled by default), we use the Fetch client unless specified otherwise.

ts title="client.gen.ts"
import { createClient, createConfig } from './client';

export const client = createClient(createConfig());

The contents of this file are consumed by SDKs, but you can also import client in your application to perform additional configuration or send manual requests.

Bundle

Client plugins provide their bundles inside client and core folders. The contents of these folders don't depend on the provided input. Everything inside these folders serves as a scaffolding so the generated code can make HTTP requests.

TypeScript

You can learn more on the TypeScript page.

SDK

You can learn more on the SDK page.

Entry File

index.ts is not generated by any specific plugin. It's meant for convenience and by default, it re-exports every artifact generated by default plugins (TypeScript and SDK).

ts title="index.ts"
export * from './sdk.gen';
export * from './types.gen';

Disable entry file

We recommend importing artifacts from their respective files to avoid ambiguity, but we leave this choice up to you.

ts title="index.ts"
import type { Pet } from './client';
// or
import type { Pet } from './client/types.gen';

If you're not importing artifacts from the entry file, you can skip generating it altogether by setting the output.entryFile option to false.

js {4} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: {
    entryFile: false,
    path: 'src/client',
  },
};

Re-export artifacts

You can choose which artifacts should be re-exported from the entry file using the includeInEntry option on any plugin. For example, we can re-export all Zod plugin artifacts by setting includeInEntry to true:

```ts title="index.ts" export { zAddPetData, zAddPetResponse, zApiResponse, zCategory, // ...more exports } from './zod.gen'; ``` ```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { includeInEntry: true, name: 'zod', }, ], }; ```

Or we can re-export only specific artifacts by providing a predicate function:

```ts title="index.ts" export { zTag } from './zod.gen'; ``` ```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { includeInEntry: (symbol) => symbol.name === 'zTag', name: 'zod', }, ], }; ```

Web Src Content Docs Docs Openapi Typescript State Management (`web/src/content/docs/docs/openapi/typescript/state-management.mdx`)


title: State Management sidebar: label: Overview

import { Badge } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';

State Management

Any reasonably large application will have to deal with state management at some point. State-related code is often one of the biggest boilerplates in your codebase. Well, at least until you start using our state management plugins.

Plugins

Hey API natively supports the following state managers.

Don't see your state manager? Let us know by opening an issue.


Web Src Content Docs Docs Openapi Typescript Validators (`web/src/content/docs/docs/openapi/typescript/validators.mdx`)


title: Validators sidebar: label: Overview

import { Badge } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';

Validators

There are times when you cannot blindly trust the server to return the correct data. You might be working on a critical application where any mistakes would be costly, or you're simply dealing with a legacy or undocumented system.

Validator plugins generate schemas from your input. You can connect them to your client as validators or transformers via the SDK plugin.

Features

  • seamless integration with @hey-api/openapi-ts ecosystem
  • schemas for requests, responses, and reusable definitions
  • static type inference from schemas
  • tree-shakeable output

Plugins

Hey API natively supports the following validators.

Don't see your validator? Let us know by opening an issue.


Web Src Content Docs Docs Openapi Typescript Web Frameworks (`web/src/content/docs/docs/openapi/typescript/web-frameworks.mdx`)


title: Web Frameworks sidebar: label: Overview

import { Badge } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';

Web Frameworks

There are two approaches to developing APIs: code-first, where you start with the code, or spec-first, where you begin with the specification. If you use the latter, you can ensure your APIs adhere to the specification with our web framework plugins.

Plugins

Hey API natively supports the following frameworks.

Don't see your framework? Let us know by opening an issue.


Web Src Content Docs Docs Openapi Python Clients Aiohttp (`web/src/content/docs/docs/openapi/python/clients/aiohttp.mdx`)


title: AIOHTTP

import FeatureStatus from '@/components/FeatureStatus.astro';

AIOHTTP

About

AIOHTTP is an asynchronous HTTP client/server for asyncio and Python.


Web Src Content Docs Docs Openapi Python Clients Custom (`web/src/content/docs/docs/openapi/python/clients/custom.mdx`)


title: Custom Client sidebar: label: Client

import Examples from '@/components/Examples.astro';

:::caution
Client API is in development. The interface might change before it becomes stable. We encourage you to leave feedback on GitHub.
:::

You may need to write your own client if the available clients do not suit your needs or you're working on a proprietary use case. This can be easily achieved using the Client API. But don't take our word for it – all Hey API clients are written this way!

:::caution
Custom clients documentation will be finalized after further testing. Simplified instructions can be found in the GitHub thread.
:::


Web Src Content Docs Docs Openapi Python Clients Httpx (`web/src/content/docs/docs/openapi/python/clients/httpx.mdx`)


title: HTTPX Client sidebar: label: HTTPX

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import StackBlitz from '@/components/StackBlitz.astro';
import UserConfig from '@/components/UserConfig.astro';

HTTPX

About

HTTPX is a fully featured HTTP client for Python 3, which provides sync and async APIs, and support for both HTTP/1.1 and HTTP/2.

The HTTPX client for Hey API generates a type-safe client from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

{/* ### Demo

<StackBlitz
example="hey-api-client-fetch-example"
openFile={[
'openapi-ts.config.ts',
'src/client/schemas.gen.ts',
'src/client/sdk.gen.ts',
'src/client/types.gen.ts',
'src/App.tsx',
]}
/> */}

Features

  • seamless integration with @hey-api/openapi-python ecosystem
  • type-safe response data and errors
  • response data validation and transformation
  • access to the original request and response
  • granular request and response customization options
  • minimal learning curve thanks to extending the underlying technology
  • support bundling inside the generated output

Installation

In your configuration, add @hey-api/client-httpx to your plugins and you'll be ready to generate client artifacts. 🎉

```js {4} title="openapi-python.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: ['@hey-api/client-httpx'], }; ``` ```sh {4} npx @hey-api/openapi-python \ -i hey-api/backend \ -o src/client \ -c @hey-api/client-httpx ```

:::note
This step is optional because HTTPX is the default client.
:::

Configuration

The HTTPX client is built as a thin wrapper on top of HTTPX, extending its functionality to work with Hey API. If you're already familiar with HTTPX, configuring your client will feel like working directly with HTTPX.

When we installed the client above, it created a client.gen.ts file. You will most likely want to configure the exported client instance. There are two ways to do that.

`setConfig()`

This is the simpler approach. You can call the setConfig() method at the beginning of your application or anytime you need to update the client configuration. You can pass any HTTPX configuration option to setConfig(), and even your own HTTPX implementation.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  baseUrl: 'https://example.com',
});

The disadvantage of this approach is that your code may call the client instance before it's configured for the first time. Depending on your use case, you might need to use the second approach.

Runtime API

Since client.gen.ts is a generated file, we can't directly modify it. Instead, we can tell our configuration to use a custom file implementing the Runtime API. We do that by specifying the runtimeConfigPath option.

js {7} title="openapi-python.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    {
      name: '@hey-api/client-httpx',
      runtimeConfigPath: './src/hey-api.ts',
    },
  ],
};

In our custom file, we need to export a createClientConfig() method. This function is a simple wrapper allowing us to override configuration values.

ts title="src/hey-api.ts"
import type { CreateClientConfig } from './client/client.gen';

export const createClientConfig: CreateClientConfig = (config) => ({
  ...config,
  baseUrl: 'https://example.com',
});

With this approach, client.gen.ts will call createClientConfig() before initializing the client instance. If needed, you can still use setConfig() to update the client configuration later.

`createClient()`

You can also create your own client instance. You can use it to manually send requests or point it to a different domain.

js title="src/index.ts"
import { createClient } from './client/client';

const myClient = createClient({
  baseUrl: 'https://example.com',
});

You can also pass this instance to any SDK function through the client option. This will override the default instance from client.gen.ts.

js title="src/index.ts"
const response = await getFoo({
  client: myClient,
});

SDKs

Alternatively, you can pass the client configuration options to each SDK function. This is useful if you don't want to create a client instance for one-off use cases.

js title="src/index.ts"
const response = await getFoo({
  baseUrl: 'https://example.com', // <-- override default configuration
});

Interceptors

Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to your application.

They can be added with use, removed with eject, and updated wth update. The use and update methods will return the ID of the interceptor for use with eject and update. Fetch API does not have the interceptor functionality, so we implement our own.

Example: Request interceptor

```js title="src/index.ts" import { client } from 'client/client.gen';

async function myInterceptor(request) {
// do something
return request;
}

interceptorId = client.interceptors.request.use(myInterceptor);

text
</TabItem>
  <TabItem label="eject">
```js title="src/index.ts"
import { client } from 'client/client.gen';

// eject by ID
client.interceptors.request.eject(interceptorId);

// eject by reference
client.interceptors.request.eject(myInterceptor);
```js title="src/index.ts" import { client } from 'client/client.gen';

async function myNewInterceptor(request) {
// do something
return request;
}

// update by ID
client.interceptors.request.update(interceptorId, myNewInterceptor);

// update by reference
client.interceptors.request.update(myInterceptor, myNewInterceptor);

text
</TabItem>
</Tabs>

### Example: Response interceptor

<Tabs>
  <TabItem label="use">
```js title="src/index.ts"
import { client } from 'client/client.gen';

async function myInterceptor(response) {
  // do something
  return response;
}

interceptorId = client.interceptors.response.use(myInterceptor);
```js title="src/index.ts" import { client } from 'client/client.gen';

// eject by ID
client.interceptors.response.eject(interceptorId);

// eject by reference
client.interceptors.response.eject(myInterceptor);

text
</TabItem>
  <TabItem label="update">
```js title="src/index.ts"
import { client } from 'client/client.gen';

async function myNewInterceptor(response) {
  // do something
  return response;
}

// update by ID
client.interceptors.response.update(interceptorId, myNewInterceptor);

// update by reference
client.interceptors.response.update(myInterceptor, myNewInterceptor);

:::tip
To eject, you must provide the ID or reference of the interceptor passed to use(), the ID is the value returned by use() and update().
:::

Auth

The SDKs include auth mechanisms for every endpoint. You will want to configure the auth field to pass the right token for each request. The auth field can be a string or a function returning a string representing the token. The returned value will be attached only to requests that require auth.

js {4} title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  auth: () => '<my_token>',
  baseUrl: 'https://example.com',
});

If you're not using SDKs or generating auth, using interceptors is a common approach to configuring auth for each request.

js {4} title="src/index.ts"
import { client } from 'client/client.gen';

client.interceptors.request.use((request, options) => {
  request.headers.set('Authorization', 'Bearer <my_token>');
  return request;
});

Build URL

If you need to access the compiled URL, you can use the buildUrl() method. It's loosely typed by default to accept almost any value; in practice, you will want to pass a type hint.

ts title="src/index.ts"
type FooData = {
  path: {
    fooId: number;
  };
  query?: {
    bar?: string;
  };
  url: '/foo/{fooId}';
};

const url = client.buildUrl<FooData>({
  path: {
    fooId: 1,
  },
  query: {
    bar: 'baz',
  },
  url: '/foo/{fooId}',
});
console.log(url); // prints '/foo/1?bar=baz'

Custom Instance

You can provide a custom fetch instance. This is useful if you need to extend the default instance with extra functionality, or replace it altogether.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  fetch: () => {
    /* custom `fetch` method */
  },
});

You can use any of the approaches mentioned in Configuration, depending on how granular you want your custom instance to be.


Web Src Content Docs Docs Openapi Python Clients Requests (`web/src/content/docs/docs/openapi/python/clients/requests.mdx`)


title: Requests

import FeatureStatus from '@/components/FeatureStatus.astro';

Requests

About

Requests is a simple, yet elegant, HTTP library.


Web Src Content Docs Docs Openapi Python Clients Urllib3 (`web/src/content/docs/docs/openapi/python/clients/urllib3.mdx`)


title: urllib3

import FeatureStatus from '@/components/FeatureStatus.astro';

urllib3

About

urllib3 is a powerful, user-friendly HTTP client for Python. urllib3 brings many critical features that are missing from the Python standard libraries.


Web Src Content Docs Docs Openapi Python Plugins Attrs (`web/src/content/docs/docs/openapi/python/plugins/attrs.mdx`)


title: Attrs

import FeatureStatus from '@/components/FeatureStatus.astro';

Attrs

About

Attrs is the Python package that will bring back the joy of writing classes by relieving you from the drudgery of implementing object protocols (aka dunder methods). Trusted by NASA for Mars missions since 2020!


Web Src Content Docs Docs Openapi Python Plugins Custom (`web/src/content/docs/docs/openapi/python/plugins/custom.mdx`)


title: Custom Plugin sidebar: label: Plugin

import { FileTree, Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';

:::caution
Plugin API is in development. The interface might change before it becomes stable. We encourage you to leave feedback on GitHub.
:::

You may need to write your own plugin if the available plugins do not suit your needs or you're working on a proprietary use case. This can be easily achieved using the Plugin API. But don't take our word for it – all Hey API plugins are written this way!

File Structure

We recommend following the design pattern of the native plugins. You can browse the code on GitHub as you follow this tutorial.

- my-plugin/ - config.ts - imports.ts - index.ts - plugin.ts - types.ts

First, create a my-plugin folder for your plugin files. Inside, create a barrel file index.ts exporting the plugin.

ts title="index.ts"
export { defaultConfig, defineConfig } from './config';
export type { MyPlugin } from './types';

Types

index.ts references two files, so we need to create them. types.ts contains the TypeScript interface for your plugin options.

ts title="types.ts"
import type { DefinePlugin, Plugin } from '@hey-api/openapi-ts';

export type UserConfig = Plugin.Name<'my-plugin'> &
  Plugin.Hooks &
  Plugin.UserExports & {
    /**
     * User-configurable option for your plugin.
     */
    user?: number | string | {
      age?: number;
      name?: string;
    };
  };

export type Config = Plugin.Name<'my-plugin'> &
  Plugin.Hooks &
  Plugin.Exports & {
    /** Resolved option for your plugin. */
    user: {
      age: number;
      name: string;
    };
  };

export type MyPlugin = DefinePlugin<UserConfig, Config>;
  • Plugin.Name sets the plugin's unique identifier.
  • Plugin.Hooks allows users to override plugin behavior through event hooks. For example, users can control how schemas are extracted or how symbols are routed to files.
  • Plugin.Exports add the includeInEntry option, which controls whether your plugin's exports are re-exported from the output entry file.
  • UserConfig is what users write in their config file. Config is the fully resolved shape your handler receives.

Configuration

config.ts defines the runtime configuration for your plugin, including how user input is resolved into Config and the handler function that generates output.

ts title="config.ts"
import { definePluginConfig } from '@hey-api/openapi-ts';

import { handler } from './plugin';
import type { MyPlugin } from './types';

export const defaultConfig: MyPlugin['Config'] = {
  config: {
    user: {
      age: 42,
      name: 'Stan Smith',
    },
  },
  handler,
  name: 'my-plugin',
};

/**
 * Type helper for `my-plugin` plugin, returns {@link Plugin.Config} object
 */
export const defineConfig = definePluginConfig(defaultConfig);

The config table is a declarative resolution spec. Each field declares its default value and optional coercion rules. To allow users to set either age or name through the user field, we can add coercion rules to transform primitive values into the expected shape.

```ts {9-12} title="config.ts" import { definePluginConfig } from '@hey-api/openapi-ts';

import { handler } from './plugin';
import type { MyPlugin } from './types';

export const defaultConfig: MyPlugin['Config'] = {
config: {
user: {
$coerce: {
number: (v) => ({ age: v }),
string: (v) => ({ name: v }),
},
age: 42,
name: 'Stan Smith',
},
},
handler,
name: 'my-plugin',
};

/**

  • Type helper for my-plugin plugin, returns {@link Plugin.Config} object
    */
    export const defineConfig = definePluginConfig(defaultConfig);
text
</TabItem>
  <TabItem label="input">
```js {8} title="openapi-ts.config.ts"
import { defineConfig } from 'path/to/my-plugin';

export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    defineConfig({
      user: 'Joe Doe', // or number to set age
    }),
  ],
};
```ts { user: { age: 42, name: 'Joe Doe' } } ```

Dependencies

Plugins can declare dependencies on other plugins. A dependency will always be included in the output, even if the user hasn't explicitly added it to their plugins config. This is useful when your plugin's output builds on another plugin's types or symbols.

ts {3} title="config.ts"
export const defaultConfig: MyPlugin['Config'] = {
  config: { ... },
  dependencies: ['@hey-api/typescript'],
  handler,
  name: 'my-plugin',
};

Resolving tags

Some dependencies aren't known at authoring time, such as which validator plugin the user has installed. Use resolveTag inside a coerce() call to look up the active plugin for a given tag at resolution time. In most cases, you'll also want to declare a dependency on the field to ensure it's included in the output.

ts {1,2,6,9} title="config.ts"
import type { PluginContext } from '@hey-api/openapi-ts';
import { coerce } from '@hey-api/openapi-ts';

export const defaultConfig: MyPlugin['Config'] = {
  config: {
    $dependencies: ['validator'],
    validator: coerce((value, context) => {
      if (value === true || value === undefined) {
        return (context as PluginContext).resolveTag('validator');
      }
      return value;
    }),
  },
  dependencies: ['@hey-api/typescript'],
  handler,
  name: 'my-plugin',
};

Handler

The handler function generates the actual output. We recommend implementing it in plugin.ts.

ts title="plugin.ts"
import { $ } from '@hey-api/openapi-ts';

import type { MyPlugin } from './types';

export const handler: MyPlugin['Handler'] = ({ plugin }) => {
  plugin.forEach('operation', 'schema', (event) => {
    if (event.type === 'operation') {
      // do something with the operation
    } else if (event.type === 'schema') {
      // do something with the schema
    }
  });

  // use the TypeScript DSL to build nodes
  const symbolName = plugin.symbol('user');
  const node = $.const(symbolName)
    .export()
    .assign($.literal(plugin.config.user.name));
  plugin.node(node);
};

Imports

Imports are a type-safe way to reference external symbols your plugin works with. Rather than querying or constructing identifiers manually, you declare them once and access them through plugin.imports anywhere in your handler.

Imports are also visible to users customizing your plugin's behavior. When a user provides hooks like $resolvers, they receive the plugin instance and can access plugin.imports to reference your plugin's imports directly.

```ts title="imports.ts" import type { PluginInstance } from '@hey-api/openapi-ts';

export function myPluginImports(plugin: PluginInstance) {
const factory = plugin.symbolFactory;
return {
myLib: factory.register('myLib', { external: 'my-lib' }),
};
}

export type MyPluginImports = ReturnType;

text
</TabItem>
  <TabItem label="types">
```ts {1,5} title="types.ts"
import type { MyPluginImports } from './imports';

/** ... */

export type MyPlugin = DefinePlugin<UserConfig, Config, never, MyPluginImports>;
```ts {1,6} title="config.ts" import { myPluginImports } from './imports';

export const defaultConfig: MyPlugin['Config'] = {
config: { ... },
handler,
imports: myPluginImports,
name: 'my-plugin',
};

text
</TabItem>
  <TabItem label="handler">
```ts {2} title="plugin.ts"
export const handler: MyPlugin['Handler'] = ({ plugin }) => {
  const { myLib } = plugin.imports;
  // use myLib as a typed reference when building nodes
};

Usage

Once you're satisfied with your plugin, register it in the configuration file.

js {1,7} title="openapi-ts.config.ts"
import { defineConfig } from 'path/to/my-plugin';

export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    defineConfig(),
  ],
};

Output

Putting all of this together will generate the following my-plugin.gen.ts file.

ts title="my-plugin.gen.ts"
export const user = 'Stan Smith';

Congratulations! You've successfully created your own plugin! 🎉


Web Src Content Docs Docs Openapi Python Plugins Dataclasses (`web/src/content/docs/docs/openapi/python/plugins/dataclasses.mdx`)


title: Dataclasses

import FeatureStatus from '@/components/FeatureStatus.astro';

Dataclasses

About

Dataclasses is a module that provides a decorator and functions for automatically adding generated special methods such as __init__() and __repr__() to user-defined classes.


Web Src Content Docs Docs Openapi Python Plugins Factory Boy (`web/src/content/docs/docs/openapi/python/plugins/factory-boy.mdx`)


title: Factory Boy

import FeatureStatus from '@/components/FeatureStatus.astro';

Factory Boy

About

Factory Boy is a fixtures replacement based on thoughtbot's factory_bot. As a fixtures replacement tool, it aims to replace static, hard to maintain fixtures with easy-to-use factories for complex objects.


Web Src Content Docs Docs Openapi Python Plugins Faker (`web/src/content/docs/docs/openapi/python/plugins/faker.mdx`)


title: Faker

import FeatureStatus from '@/components/FeatureStatus.astro';

Faker

About

Faker is a Python package that generates fake data for you. Whether you need to bootstrap your database, create good-looking XML documents, fill-in your persistence to stress test it, or anonymize data taken from a production service, Faker is for you.


Web Src Content Docs Docs Openapi Python Plugins Marshmallow (`web/src/content/docs/docs/openapi/python/plugins/marshmallow.mdx`)


title: Marshmallow

import FeatureStatus from '@/components/FeatureStatus.astro';

Marshmallow

About

Marshmallow is an ORM/ODM/framework-agnostic library for converting complex datatypes, such as objects, to and from native Python datatypes.


Web Src Content Docs Docs Openapi Python Plugins Mimesis (`web/src/content/docs/docs/openapi/python/plugins/mimesis.mdx`)


title: Mimesis

import FeatureStatus from '@/components/FeatureStatus.astro';

Mimesis

About

Mimesis is a powerful data generator for Python that can produce a wide range of fake data in multiple languages.


Web Src Content Docs Docs Openapi Python Plugins Pydantic (`web/src/content/docs/docs/openapi/python/plugins/pydantic.mdx`)


title: Pydantic v2 Plugin sidebar: label: Pydantic custom: version: v2

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import UserConfig from '@/components/UserConfig.astro';

Pydantic

About

Pydantic is the most widely used data validation library for Python. Fast and extensible, Pydantic plays nicely with your linters/IDE/brain.

The Pydantic plugin for Hey API generates models from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

Features

  • Pydantic v2 support
  • seamless integration with @hey-api/openapi-python ecosystem
  • Pydantic models for requests, responses, and reusable definitions
  • minimal learning curve thanks to extending the underlying technology

Installation

In your configuration, add pydantic to your plugins and you'll be ready to generate Pydantic artifacts. 🎉

js {6} title="openapi-python.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    'pydantic',
  ],
};

SDKs

Pydantic can validate or transform data directly in your SDK. Set validator or transformer to true on the SDK plugin to enable it.

```js {7-10} title="openapi-python.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins 'pydantic', { name: '@hey-api/sdk', validator: true, }, ], }; ``` ```js {7-10} title="openapi-python.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins 'pydantic', { name: '@hey-api/sdk', transformer: true, }, ], }; ```

The SDK page covers validators and transformers in more detail.

Output

The Pydantic plugin will generate the following artifacts, depending on the input specification.

Requests

A Pydantic model is generated for every request layer of each endpoint.

```ts title="pydantic.gen.ts" const vDeletePetHeaders = v.object({ api_key: v.optional(v.string()), });

const vDeletePetPath = v.object({
petId: v.number(),
});

const vDeletePetQuery = v.object({
additionalMetadata: v.string(),
});

text
</TabItem>
  <TabItem label="config">
```js {8} title="openapi-python.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'pydantic',
      requests: true,
    },
  ],
};

You can customize the naming and casing pattern for requests schemas using the .name and .case options.

Responses

A single Pydantic model is generated for all endpoint's responses. If the endpoint describes multiple responses, the generated model is a union of all possible response shapes.

```ts title="pydantic.gen.ts" const vResponse = v.union([ v.object({ foo: v.optional(v.string()), }), v.object({ bar: v.optional(v.number()), }), ]); ``` ```js {8} title="openapi-python.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'pydantic', responses: true, }, ], }; ```

You can customize the naming and casing pattern for responses schemas using the .name and .case options.

Definitions

A Pydantic model is generated for every reusable definition from your input.

```ts title="pydantic.gen.ts" const vFoo = v.pipe(v.number(), v.integer());

const vBar = v.object({
bar: v.optional(v.array(v.pipe(v.number(), v.integer()))),
});

text
</TabItem>
  <TabItem label="config">
```js {8} title="openapi-python.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'pydantic',
      definitions: true,
    },
  ],
};

You can customize the naming and casing pattern for definitions schemas using the .name and .case options.

Metadata

It's often useful to associate a schema with some additional metadata for documentation, code generation, AI structured outputs, form validation, and other purposes. You can set metadata to true to attach descriptions to schemas when available.

```ts title="pydantic.gen.ts" export const vFoo = v.pipe( v.string(), v.metadata({ description: 'Additional metadata', }), ); ``` ```js {8} title="openapi-python.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'pydantic', metadata: true, }, ], }; ```

For more control over metadata, you can provide your own function. It receives the source schema, the target node object, and the $ builder for populating metadata.

```ts title="pydantic.gen.ts" export const vFoo = v.pipe( v.string(), v.metadata({ hasTitle: false, createdAt: 1735732800000, }), ); ``` ```js {8-11} title="openapi-python.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'pydantic', metadata({ $, node, schema }) { node.prop('hasTitle', $.literal(Boolean(schema.title))); node.prop('createdAt', $.literal(Date.now())); }, }, ], }; ```

Resolvers

You can further customize this plugin's behavior using resolvers.

{/* */}


Web Src Content Docs Docs Openapi Typescript Clients Axios (`web/src/content/docs/docs/openapi/typescript/clients/axios.mdx`)


title: Axios v1 Client sidebar: label: Axios custom: version: v1

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import StackBlitz from '@/components/StackBlitz.astro';
import UserConfig from '@/components/UserConfig.astro';

Axios

About

Axios is a simple promise based HTTP client for the browser and Node.js. Axios provides a simple to use library in a small package with a very extensible interface.

The Axios client for Hey API generates a type-safe client from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

Demo

<StackBlitz
example="hey-api-client-axios-example"
openFile={[
'openapi-ts.config.ts',
'src/client/schemas.gen.ts',
'src/client/sdk.gen.ts',
'src/client/types.gen.ts',
'src/App.tsx',
]}
/>

Features

  • Axios v1 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • type-safe response data and errors
  • response data validation and transformation
  • access to the original request and response
  • granular request and response customization options
  • minimal learning curve thanks to extending the underlying technology
  • support bundling inside the generated output

Installation

In your configuration, add @hey-api/client-axios to your plugins and you'll be ready to generate client artifacts. 🎉

```js {4} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: ['@hey-api/client-axios'], }; ``` ```sh {4} npx @hey-api/openapi-ts \ -i hey-api/backend \ -o src/client \ -c @hey-api/client-axios ```

Configuration

The Axios client is built as a thin wrapper on top of Axios, extending its functionality to work with Hey API. If you're already familiar with Axios, configuring your client will feel like working directly with Axios.

When we installed the client above, it created a client.gen.ts file. You will most likely want to configure the exported client instance. There are two ways to do that.

`setConfig()`

This is the simpler approach. You can call the setConfig() method at the beginning of your application or anytime you need to update the client configuration. You can pass any Axios configuration option to setConfig() (except for auth), and even your own Axios implementation.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  baseURL: 'https://example.com',
});

The disadvantage of this approach is that your code may call the client instance before it's configured for the first time. Depending on your use case, you might need to use the second approach.

Runtime API

Since client.gen.ts is a generated file, we can't directly modify it. Instead, we can tell our configuration to use a custom file implementing the Runtime API. We do that by specifying the runtimeConfigPath option.

js {7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    {
      name: '@hey-api/client-axios',
      runtimeConfigPath: './src/hey-api.ts',
    },
  ],
};

In our custom file, we need to export a createClientConfig() method. This function is a simple wrapper allowing us to override configuration values.

ts title="src/hey-api.ts"
import type { CreateClientConfig } from './client/client.gen';

export const createClientConfig: CreateClientConfig = (config) => ({
  ...config,
  baseURL: 'https://example.com',
});

With this approach, client.gen.ts will call createClientConfig() before initializing the client instance. If needed, you can still use setConfig() to update the client configuration later.

`createClient()`

You can also create your own client instance. You can use it to manually send requests or point it to a different domain.

js title="src/index.ts"
import { createClient } from './client/client';

const myClient = createClient({
  baseURL: 'https://example.com',
});

You can also pass this instance to any SDK function through the client option. This will override the default instance from client.gen.ts.

js title="src/index.ts"
const response = await getFoo({
  client: myClient,
});

SDKs

Alternatively, you can pass the client configuration options to each SDK function. This is useful if you don't want to create a client instance for one-off use cases.

js title="src/index.ts"
const response = await getFoo({
  baseURL: 'https://example.com', // <-- override default configuration
});

Interceptors

Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to your application. Axios provides interceptors, please refer to their documentation on interceptors.

We expose the Axios instance through the instance field.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.instance.interceptors.request.use((config) => {
  // do something
  return config;
});

Auth

The SDKs include auth mechanisms for every endpoint. You will want to configure the auth field to pass the right token for each request. The auth field can be a string or a function returning a string representing the token. The returned value will be attached only to requests that require auth.

js {4} title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  auth: () => '<my_token>',
  baseURL: 'https://example.com',
});

If you're not using SDKs or generating auth, using interceptors is a common approach to configuring auth for each request.

js {4} title="src/index.ts"
import { client } from 'client/client.gen';

client.instance.interceptors.request.use((config) => {
  config.headers.set('Authorization', 'Bearer <my_token>');
  return config;
});

Build URL

If you need to access the compiled URL, you can use the buildUrl() method. It's loosely typed by default to accept almost any value; in practice, you will want to pass a type hint.

ts title="src/index.ts"
type FooData = {
  path: {
    fooId: number;
  };
  query?: {
    bar?: string;
  };
  url: '/foo/{fooId}';
};

const url = client.buildUrl<FooData>({
  path: {
    fooId: 1,
  },
  query: {
    bar: 'baz',
  },
  url: '/foo/{fooId}',
});
console.log(url); // prints '/foo/1?bar=baz'

Custom Instance

You can provide a custom axios instance. This is useful if you need to extend the default instance with extra functionality, or replace it altogether.

js title="src/index.ts"
import axios from 'axios';
import { client } from 'client/client.gen';

// Customize the default axios instance
axios.defaults.baseURL = 'https://example.com';

client.setConfig({
  axios: axios,
});

or you can pass an AxiosInstance created with axios.create():

js title="src/index.ts"
import axios from 'axios';
import { client } from 'client/client.gen';

const customAxiosInstance = axios.create({
  baseURL: 'https://example.com',
});

client.setConfig({
  axios: customAxiosInstance,
});

You can use any of the approaches mentioned in Configuration, depending on how granular you want your custom instance to be.


Web Src Content Docs Docs Openapi Typescript Clients Custom (`web/src/content/docs/docs/openapi/typescript/clients/custom.mdx`)


title: Custom Client sidebar: label: Client

import Examples from '@/components/Examples.astro';

:::caution
Client API is in development. The interface might change before it becomes stable. We encourage you to leave feedback on GitHub.
:::

You may need to write your own client if the available clients do not suit your needs or you're working on a proprietary use case. This can be easily achieved using the Client API. But don't take our word for it – all Hey API clients are written this way!

:::caution
Custom clients documentation will be finalized after further testing. Simplified instructions can be found in the GitHub thread.
:::


Web Src Content Docs Docs Openapi Typescript Clients Effect (`web/src/content/docs/docs/openapi/typescript/clients/effect.mdx`)


title: Effect client sidebar: label: Effect

import FeatureStatus from '@/components/FeatureStatus.astro';

Effect

About

Effect is a powerful TypeScript library designed to help developers easily create complex, synchronous, and asynchronous programs.


Web Src Content Docs Docs Openapi Typescript Clients Fetch (`web/src/content/docs/docs/openapi/typescript/clients/fetch.mdx`)


title: Fetch API Client sidebar: label: Fetch API

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import StackBlitz from '@/components/StackBlitz.astro';
import UserConfig from '@/components/UserConfig.astro';

Fetch API

About

The Fetch API provides an interface for fetching resources (including across the network). It is a more powerful and flexible replacement for XMLHttpRequest.

The Fetch API client for Hey API generates a type-safe client from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

Demo

<StackBlitz
example="hey-api-client-fetch-example"
openFile={[
'openapi-ts.config.ts',
'src/client/schemas.gen.ts',
'src/client/sdk.gen.ts',
'src/client/types.gen.ts',
'src/App.tsx',
]}
/>

Features

  • seamless integration with @hey-api/openapi-ts ecosystem
  • type-safe response data and errors
  • response data validation and transformation
  • access to the original request and response
  • granular request and response customization options
  • minimal learning curve thanks to extending the underlying technology
  • support bundling inside the generated output

Installation

In your configuration, add @hey-api/client-fetch to your plugins and you'll be ready to generate client artifacts. 🎉

```js {4} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: ['@hey-api/client-fetch'], }; ``` ```sh {4} npx @hey-api/openapi-ts \ -i hey-api/backend \ -o src/client \ -c @hey-api/client-fetch ```

:::note
This step is optional because Fetch is the default client.
:::

Configuration

The Fetch client is built as a thin wrapper on top of Fetch API, extending its functionality to work with Hey API. If you're already familiar with Fetch, configuring your client will feel like working directly with Fetch API.

When we installed the client above, it created a client.gen.ts file. You will most likely want to configure the exported client instance. There are two ways to do that.

`setConfig()`

This is the simpler approach. You can call the setConfig() method at the beginning of your application or anytime you need to update the client configuration. You can pass any Fetch API configuration option to setConfig(), and even your own Fetch implementation.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  baseUrl: 'https://example.com',
});

The disadvantage of this approach is that your code may call the client instance before it's configured for the first time. Depending on your use case, you might need to use the second approach.

Runtime API

Since client.gen.ts is a generated file, we can't directly modify it. Instead, we can tell our configuration to use a custom file implementing the Runtime API. We do that by specifying the runtimeConfigPath option.

js {7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    {
      name: '@hey-api/client-fetch',
      runtimeConfigPath: './src/hey-api.ts',
    },
  ],
};

In our custom file, we need to export a createClientConfig() method. This function is a simple wrapper allowing us to override configuration values.

ts title="src/hey-api.ts"
import type { CreateClientConfig } from './client/client.gen';

export const createClientConfig: CreateClientConfig = (config) => ({
  ...config,
  baseUrl: 'https://example.com',
});

With this approach, client.gen.ts will call createClientConfig() before initializing the client instance. If needed, you can still use setConfig() to update the client configuration later.

`createClient()`

You can also create your own client instance. You can use it to manually send requests or point it to a different domain.

js title="src/index.ts"
import { createClient } from './client/client';

const myClient = createClient({
  baseUrl: 'https://example.com',
});

You can also pass this instance to any SDK function through the client option. This will override the default instance from client.gen.ts.

js title="src/index.ts"
const response = await getFoo({
  client: myClient,
});

SDKs

Alternatively, you can pass the client configuration options to each SDK function. This is useful if you don't want to create a client instance for one-off use cases.

js title="src/index.ts"
const response = await getFoo({
  baseUrl: 'https://example.com', // <-- override default configuration
});

Interceptors

Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to your application.

They can be added with use, removed with eject, and updated wth update. The use and update methods will return the ID of the interceptor for use with eject and update. Fetch API does not have the interceptor functionality, so we implement our own.

Example: Request interceptor

```js title="src/index.ts" import { client } from 'client/client.gen';

async function myInterceptor(request) {
// do something
return request;
}

interceptorId = client.interceptors.request.use(myInterceptor);

text
</TabItem>
  <TabItem label="eject">
```js title="src/index.ts"
import { client } from 'client/client.gen';

// eject by ID
client.interceptors.request.eject(interceptorId);

// eject by reference
client.interceptors.request.eject(myInterceptor);
```js title="src/index.ts" import { client } from 'client/client.gen';

async function myNewInterceptor(request) {
// do something
return request;
}

// update by ID
client.interceptors.request.update(interceptorId, myNewInterceptor);

// update by reference
client.interceptors.request.update(myInterceptor, myNewInterceptor);

text
</TabItem>
</Tabs>

### Example: Response interceptor

<Tabs>
  <TabItem label="use">
```js title="src/index.ts"
import { client } from 'client/client.gen';

async function myInterceptor(response) {
  // do something
  return response;
}

interceptorId = client.interceptors.response.use(myInterceptor);
```js title="src/index.ts" import { client } from 'client/client.gen';

// eject by ID
client.interceptors.response.eject(interceptorId);

// eject by reference
client.interceptors.response.eject(myInterceptor);

text
</TabItem>
  <TabItem label="update">
```js title="src/index.ts"
import { client } from 'client/client.gen';

async function myNewInterceptor(response) {
  // do something
  return response;
}

// update by ID
client.interceptors.response.update(interceptorId, myNewInterceptor);

// update by reference
client.interceptors.response.update(myInterceptor, myNewInterceptor);

:::tip
To eject, you must provide the ID or reference of the interceptor passed to use(), the ID is the value returned by use() and update().
:::

Auth

The SDKs include auth mechanisms for every endpoint. You will want to configure the auth field to pass the right token for each request. The auth field can be a string or a function returning a string representing the token. The returned value will be attached only to requests that require auth.

js {4} title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  auth: () => '<my_token>',
  baseUrl: 'https://example.com',
});

If you're not using SDKs or generating auth, using interceptors is a common approach to configuring auth for each request.

js {4} title="src/index.ts"
import { client } from 'client/client.gen';

client.interceptors.request.use((request, options) => {
  request.headers.set('Authorization', 'Bearer <my_token>');
  return request;
});

Build URL

If you need to access the compiled URL, you can use the buildUrl() method. It's loosely typed by default to accept almost any value; in practice, you will want to pass a type hint.

ts title="src/index.ts"
type FooData = {
  path: {
    fooId: number;
  };
  query?: {
    bar?: string;
  };
  url: '/foo/{fooId}';
};

const url = client.buildUrl<FooData>({
  path: {
    fooId: 1,
  },
  query: {
    bar: 'baz',
  },
  url: '/foo/{fooId}',
});
console.log(url); // prints '/foo/1?bar=baz'

Custom Instance

You can provide a custom fetch instance. This is useful if you need to extend the default instance with extra functionality, or replace it altogether.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  fetch: () => {
    /* custom `fetch` method */
  },
});

You can use any of the approaches mentioned in Configuration, depending on how granular you want your custom instance to be.


Web Src Content Docs Docs Openapi Typescript Clients Got (`web/src/content/docs/docs/openapi/typescript/clients/got.mdx`)


title: Got client sidebar: label: Got

import FeatureStatus from '@/components/FeatureStatus.astro';

Got

About

Got is a human-friendly and powerful HTTP request library for Node.js.


Web Src Content Docs Docs Openapi Typescript Clients Ky (`web/src/content/docs/docs/openapi/typescript/clients/ky.mdx`)


title: Ky v1 Client sidebar: label: Ky custom: version: v1

import { Tabs, TabItem } from '@astrojs/starlight/components';
import AuthorsList from '@/components/AuthorsList.astro';
import Examples from '@/components/Examples.astro';
import UserConfig from '@/components/UserConfig.astro';
import { sebastiaanWouters } from '@/data/people';

Ky

About

Ky is a tiny and elegant JavaScript HTTP client based on the Fetch API.

The Ky client for Hey API generates a type-safe client from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

Collaborators

Features

  • seamless integration with @hey-api/openapi-ts ecosystem
  • type-safe response data and errors
  • response data validation and transformation
  • access to the original request and response
  • granular request and response customization options
  • minimal learning curve thanks to extending the underlying technology
  • support bundling inside the generated output

Installation

In your configuration, add @hey-api/client-ky to your plugins and you'll be ready to generate client artifacts. 🎉

```js {4} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: ['@hey-api/client-ky'], }; ``` ```sh {4} npx @hey-api/openapi-ts \ -i hey-api/backend \ -o src/client \ -c @hey-api/client-ky ```

Configuration

The Ky client is built as a thin wrapper on top of Ky, extending its functionality to work with Hey API. If you're already familiar with Ky, configuring your client will feel like working directly with Ky.

When we installed the client above, it created a client.gen.ts file. You will most likely want to configure the exported client instance. There are two ways to do that.

`setConfig()`

This is the simpler approach. You can call the setConfig() method at the beginning of your application or anytime you need to update the client configuration. You can pass any Ky configuration option to setConfig(), and even your own ky instance.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  baseUrl: 'https://example.com',
});

The disadvantage of this approach is that your code may call the client instance before it's configured for the first time. Depending on your use case, you might need to use the second approach.

Runtime API

Since client.gen.ts is a generated file, we can't directly modify it. Instead, we can tell our configuration to use a custom file implementing the Runtime API. We do that by specifying the runtimeConfigPath option.

js {7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    {
      name: '@hey-api/client-ky',
      runtimeConfigPath: './src/hey-api.ts',
    },
  ],
};

In our custom file, we need to export a createClientConfig() method. This function is a simple wrapper allowing us to override configuration values.

ts title="src/hey-api.ts"
import type { CreateClientConfig } from './client/client.gen';

export const createClientConfig: CreateClientConfig = (config) => ({
  ...config,
  baseUrl: 'https://example.com',
});

With this approach, client.gen.ts will call createClientConfig() before initializing the client instance. If needed, you can still use setConfig() to update the client configuration later.

`createClient()`

You can also create your own client instance. You can use it to manually send requests or point it to a different domain.

js title="src/index.ts"
import { createClient } from './client/client';

const myClient = createClient({
  baseUrl: 'https://example.com',
});

You can also pass this instance to any SDK function through the client option. This will override the default instance from client.gen.ts.

js title="src/index.ts"
const response = await getFoo({
  client: myClient,
});

SDKs

Alternatively, you can pass the client configuration options to each SDK function. This is useful if you don't want to create a client instance for one-off use cases.

js title="src/index.ts"
const response = await getFoo({
  baseUrl: 'https://example.com', // <-- override default configuration
});

Interceptors

Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to your application.

They can be added with use, removed with eject, and updated wth update. The use and update methods will return the ID of the interceptor for use with eject and update. Ky does not have the interceptor functionality, so we implement our own.

Example: Request interceptor

```js title="src/index.ts" import { client } from 'client/client.gen';

async function myInterceptor(request) {
// do something
return request;
}

interceptorId = client.interceptors.request.use(myInterceptor);

text
</TabItem>
  <TabItem label="eject">
```js title="src/index.ts"
import { client } from 'client/client.gen';

// eject by ID
client.interceptors.request.eject(interceptorId);

// eject by reference
client.interceptors.request.eject(myInterceptor);
```js title="src/index.ts" import { client } from 'client/client.gen';

async function myNewInterceptor(request) {
// do something
return request;
}

// update by ID
client.interceptors.request.update(interceptorId, myNewInterceptor);

// update by reference
client.interceptors.request.update(myInterceptor, myNewInterceptor);

text
</TabItem>
</Tabs>

### Example: Response interceptor

<Tabs>
  <TabItem label="use">
```js title="src/index.ts"
import { client } from 'client/client.gen';

async function myInterceptor(response) {
  // do something
  return response;
}

interceptorId = client.interceptors.response.use(myInterceptor);
```js title="src/index.ts" import { client } from 'client/client.gen';

// eject by ID
client.interceptors.response.eject(interceptorId);

// eject by reference
client.interceptors.response.eject(myInterceptor);

text
</TabItem>
  <TabItem label="update">
```js title="src/index.ts"
import { client } from 'client/client.gen';

async function myNewInterceptor(response) {
  // do something
  return response;
}

// update by ID
client.interceptors.response.update(interceptorId, myNewInterceptor);

// update by reference
client.interceptors.response.update(myInterceptor, myNewInterceptor);

:::tip
To eject, you must provide the ID or reference of the interceptor passed to use(), the ID is the value returned by use() and update().
:::

Auth

The SDKs include auth mechanisms for every endpoint. You will want to configure the auth field to pass the right token for each request. The auth field can be a string or a function returning a string representing the token. The returned value will be attached only to requests that require auth.

js {4} title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  auth: () => '<my_token>',
  baseUrl: 'https://example.com',
});

If you're not using SDKs or generating auth, using interceptors is a common approach to configuring auth for each request.

js {4} title="src/index.ts"
import { client } from 'client/client.gen';

client.interceptors.request.use((request, options) => {
  request.headers.set('Authorization', 'Bearer <my_token>');
  return request;
});

Build URL

If you need to access the compiled URL, you can use the buildUrl() method. It's loosely typed by default to accept almost any value; in practice, you will want to pass a type hint.

ts title="src/index.ts"
type FooData = {
  path: {
    fooId: number;
  };
  query?: {
    bar?: string;
  };
  url: '/foo/{fooId}';
};

const url = client.buildUrl<FooData>({
  path: {
    fooId: 1,
  },
  query: {
    bar: 'baz',
  },
  url: '/foo/{fooId}',
});
console.log(url); // prints '/foo/1?bar=baz'

Custom Instance

You can provide a custom ky instance. This is useful if you need to extend the default instance with extra functionality, or replace it altogether.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  ky: ky.create({
    /* custom `ky` instance */
  }),
});

You can use any of the approaches mentioned in Configuration, depending on how granular you want your custom instance to be.


Web Src Content Docs Docs Openapi Typescript Clients Next Js (`web/src/content/docs/docs/openapi/typescript/clients/next-js.mdx`)


title: Next.js Client sidebar: label: Next.js

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import UserConfig from '@/components/UserConfig.astro';

Next.js

About

Next.js is the React framework for the web. Used by some of the world's largest companies, Next.js enables you to create high-quality web applications with the power of React components.

The Next.js client for Hey API generates a type-safe client from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

Features

  • seamless integration with @hey-api/openapi-ts ecosystem
  • type-safe response data and errors
  • response data validation and transformation
  • access to the original request and response
  • granular request and response customization options
  • minimal learning curve thanks to extending the underlying technology
  • support bundling inside the generated output

Installation

In your configuration, add @hey-api/client-next to your plugins and you'll be ready to generate client artifacts. 🎉

```js {4} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: ['@hey-api/client-next'], }; ``` ```sh {4} npx @hey-api/openapi-ts \ -i hey-api/backend \ -o src/client \ -c @hey-api/client-next ```

Configuration

The Next.js client is built as a thin wrapper on top of fetch, extending its functionality to work with Hey API. If you're already familiar with Fetch, configuring your client will feel like working directly with Fetch API.

When we installed the client above, it created a client.gen.ts file. You will most likely want to configure the exported client instance. There are two ways to do that.

Runtime API

Since client.gen.ts is a generated file, we can't directly modify it. Instead, we can tell our configuration to use a custom file implementing the Runtime API. We do that by specifying the runtimeConfigPath option.

js {7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    {
      name: '@hey-api/client-next',
      runtimeConfigPath: './src/hey-api.ts',
    },
  ],
};

In our custom file, we need to export a createClientConfig() method. This function is a simple wrapper allowing us to override configuration values.

ts title="src/hey-api.ts"
import type { CreateClientConfig } from './client/client.gen';

export const createClientConfig: CreateClientConfig = (config) => ({
  ...config,
  baseUrl: 'https://example.com',
});

With this approach, client.gen.ts will call createClientConfig() before initializing the client instance. This is the recommended approach because it guarantees the client will be initialized in both server and client environment. If needed, you can still use setConfig() to update the client configuration later.

`setConfig()`

This is the simpler approach. You can call the setConfig() method at the beginning of your application or anytime you need to update the client configuration. You can pass any Fetch API configuration option to setConfig(), and even your own Fetch implementation.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  baseUrl: 'https://example.com',
});

The disadvantage of this approach is that your code may call the client instance before it's configured for the first time. Depending on your use case, this might be an acceptable trade-off. However, our Next.js users usually want to use the first approach.

`createClient()`

You can also create your own client instance. You can use it to manually send requests or point it to a different domain.

js title="src/index.ts"
import { createClient } from './client/client';

const myClient = createClient({
  baseUrl: 'https://example.com',
});

You can also pass this instance to any SDK function through the client option. This will override the default instance from client.gen.ts.

js title="src/index.ts"
const response = await getFoo({
  client: myClient,
});

SDKs

Alternatively, you can pass the client configuration options to each SDK function. This is useful if you don't want to create a client instance for one-off use cases.

js title="src/index.ts"
const response = await getFoo({
  baseUrl: 'https://example.com', // <-- override default configuration
});

Interceptors

Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to your application.

They can be added with use, removed with eject, and updated wth update. The use and update methods will return the ID of the interceptor for use with eject and update. Fetch API does not have the interceptor functionality, so we implement our own.

Example: Request interceptor

```js title="src/index.ts" import { client } from 'client/client.gen';

async function myInterceptor(request) {
// do something
return request;
}

interceptorId = client.interceptors.request.use(myInterceptor);

text
</TabItem>
  <TabItem label="eject">
```js title="src/index.ts"
import { client } from 'client/client.gen';

// eject by ID
client.interceptors.request.eject(interceptorId);

// eject by reference
client.interceptors.request.eject(myInterceptor);
```js title="src/index.ts" import { client } from 'client/client.gen';

async function myNewInterceptor(request) {
// do something
return request;
}

// update by ID
client.interceptors.request.update(interceptorId, myNewInterceptor);

// update by reference
client.interceptors.request.update(myInterceptor, myNewInterceptor);

text
</TabItem>
</Tabs>

### Example: Response interceptor

<Tabs>
  <TabItem label="use">
```js title="src/index.ts"
import { client } from 'client/client.gen';

async function myInterceptor(response) {
  // do something
  return response;
}

interceptorId = client.interceptors.response.use(myInterceptor);
```js title="src/index.ts" import { client } from 'client/client.gen';

// eject by ID
client.interceptors.response.eject(interceptorId);

// eject by reference
client.interceptors.response.eject(myInterceptor);

text
</TabItem>
  <TabItem label="update">
```js title="src/index.ts"
import { client } from 'client/client.gen';

async function myNewInterceptor(response) {
  // do something
  return response;
}

// update by ID
client.interceptors.response.update(interceptorId, myNewInterceptor);

// update by reference
client.interceptors.response.update(myInterceptor, myNewInterceptor);

:::tip
To eject, you must provide the ID or reference of the interceptor passed to use(), the ID is the value returned by use() and update().
:::

Auth

The SDKs include auth mechanisms for every endpoint. You will want to configure the auth field to pass the right token for each request. The auth field can be a string or a function returning a string representing the token. The returned value will be attached only to requests that require auth.

js {4} title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  auth: () => '<my_token>',
  baseUrl: 'https://example.com',
});

If you're not using SDKs or generating auth, using interceptors is a common approach to configuring auth for each request.

js {4} title="src/index.ts"
import { client } from 'client/client.gen';

client.interceptors.request.use((options) => {
  options.headers.set('Authorization', 'Bearer <my_token>');
});

Build URL

If you need to access the compiled URL, you can use the buildUrl() method. It's loosely typed by default to accept almost any value; in practice, you will want to pass a type hint.

ts title="src/index.ts"
type FooData = {
  path: {
    fooId: number;
  };
  query?: {
    bar?: string;
  };
  url: '/foo/{fooId}';
};

const url = client.buildUrl<FooData>({
  path: {
    fooId: 1,
  },
  query: {
    bar: 'baz',
  },
  url: '/foo/{fooId}',
});
console.log(url); // prints '/foo/1?bar=baz'

Custom Instance

You can provide a custom fetch instance. This is useful if you need to extend the default instance with extra functionality, or replace it altogether.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  fetch: () => {
    /* custom `fetch` method */
  },
});

You can use any of the approaches mentioned in Configuration, depending on how granular you want your custom instance to be.


Web Src Content Docs Docs Openapi Typescript Clients Nuxt (`web/src/content/docs/docs/openapi/typescript/clients/nuxt.mdx`)


title: Nuxt v3 Client sidebar: label: Nuxt custom: version: v3

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import FeatureStatus from '@/components/FeatureStatus.astro';
import InstallCommand from '@/components/package-managers/InstallCommand.astro';
import UserConfig from '@/components/UserConfig.astro';

Nuxt

About

Nuxt is an open source framework that makes web development intuitive and powerful.

The Nuxt client for Hey API generates a type-safe client from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

Features

  • Nuxt v3 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • type-safe response data and errors
  • response data validation and transformation
  • access to the original request and response
  • granular request and response customization options
  • minimal learning curve thanks to extending the underlying technology
  • support bundling inside the generated output

Installation

Start by adding @hey-api/nuxt to your dependencies.

In your configuration, add @hey-api/client-nuxt to your plugins and you'll be ready to generate client artifacts. 🎉

```js {4} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: ['@hey-api/client-nuxt'], }; ``` ```sh {4} npx @hey-api/openapi-ts \ -i hey-api/backend \ -o src/client \ -c @hey-api/client-nuxt ```

:::tip
If you add @hey-api/nuxt to your Nuxt modules, this step is not needed.
:::

Configuration

The Nuxt client is built as a thin wrapper on top of Nuxt, extending its functionality to work with Hey API. If you're already familiar with Nuxt, configuring your client will feel like working directly with Nuxt.

When we installed the client above, it created a client.gen.ts file. You will most likely want to configure the exported client instance. There are two ways to do that.

`setConfig()`

This is the simpler approach. You can call the setConfig() method at the beginning of your application or anytime you need to update the client configuration. You can pass any Nuxt configuration option to setConfig(), and even your own $fetch implementation.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  baseURL: 'https://example.com',
});

The disadvantage of this approach is that your code may call the client instance before it's configured for the first time. Depending on your use case, you might need to use the second approach.

Runtime API

Since client.gen.ts is a generated file, we can't directly modify it. Instead, we can tell our configuration to use a custom file implementing the Runtime API. We do that by specifying the runtimeConfigPath option.

js {7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    {
      name: '@hey-api/client-nuxt',
      runtimeConfigPath: './src/hey-api.ts',
    },
  ],
};

In our custom file, we need to export a createClientConfig() method. This function is a simple wrapper allowing us to override configuration values.

ts title="src/hey-api.ts"
import type { CreateClientConfig } from './client/client.gen';

export const createClientConfig: CreateClientConfig = (config) => ({
  ...config,
  baseURL: 'https://example.com',
});

With this approach, client.gen.ts will call createClientConfig() before initializing the client instance. If needed, you can still use setConfig() to update the client configuration later.

`createClient()`

You can also create your own client instance. You can use it to manually send requests or point it to a different domain.

js title="src/index.ts"
import { createClient } from './client/client';

const myClient = createClient({
  baseURL: 'https://example.com',
});

You can also pass this instance to any SDK function through the client option. This will override the default instance from client.gen.ts.

js title="src/index.ts"
const response = await getFoo({
  client: myClient,
});

SDKs

Alternatively, you can pass the client configuration options to each SDK function. This is useful if you don't want to create a client instance for one-off use cases.

js title="src/index.ts"
const response = await getFoo({
  baseURL: 'https://example.com', // <-- override default configuration
});

Interceptors

Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to your application. Nuxt provides interceptors through ofetch, please refer to their documentation on $fetch.

You can pass any Nuxt/ofetch arguments to the client instance.

:::note
If you omit composable, $fetch is used by default.
:::

js title="src/index.ts"
import { client } from 'client/client.gen';

const result = await client.get({
  composable: '$fetch',
  onRequest: (context) => {
    // do something
  },
  url: '/foo',
});

Auth

The SDKs include auth mechanisms for every endpoint. You will want to configure the auth field to pass the right token for each request. The auth field can be a string or a function returning a string representing the token. The returned value will be attached only to requests that require auth.

js {4} title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  auth: () => '<my_token>',
  baseURL: 'https://example.com',
});

If you're not using SDKs or generating auth, using interceptors is a common approach to configuring auth for each request.

js {5} title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  onRequest: ({ options }) => {
    options.headers.set('Authorization', 'Bearer <my_token>');
  },
});

Build URL

If you need to access the compiled URL, you can use the buildUrl() method. It's loosely typed by default to accept almost any value; in practice, you will want to pass a type hint.

ts title="src/index.ts"
type FooData = {
  path: {
    fooId: number;
  };
  query?: {
    bar?: string;
  };
  url: '/foo/{fooId}';
};

const url = client.buildUrl<FooData>({
  path: {
    fooId: 1,
  },
  query: {
    bar: 'baz',
  },
  url: '/foo/{fooId}',
});
console.log(url); // prints '/foo/1?bar=baz'

Custom Instance

You can provide a custom $fetch instance. This is useful if you need to extend the default instance with extra functionality, or replace it altogether.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  $fetch: () => {
    /* custom `$fetch` method */
  },
});

You can use any of the approaches mentioned in Configuration, depending on how granular you want your custom instance to be.


Web Src Content Docs Docs Openapi Typescript Clients Ofetch (`web/src/content/docs/docs/openapi/typescript/clients/ofetch.mdx`)


title: OFetch Client sidebar: label: OFetch

import { Tabs, TabItem } from '@astrojs/starlight/components';
import AuthorsList from '@/components/AuthorsList.astro';
import Examples from '@/components/Examples.astro';
import UserConfig from '@/components/UserConfig.astro';
import { dmitriyBrolnickij } from '@/data/people';

OFetch

About

ofetch is a better Fetch API that adds useful defaults and features such as automatic response parsing, request/response hooks, and it works in Node, browser, and workers.

The ofetch client for Hey API generates a type-safe client from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

Collaborators

Features

  • seamless integration with @hey-api/openapi-ts ecosystem
  • type-safe response data and errors
  • response data validation and transformation
  • access to the original request and response
  • granular request and response customization options
  • minimal learning curve thanks to extending the underlying technology
  • support bundling inside the generated output

Installation

In your configuration, add @hey-api/client-ofetch to your plugins and you'll be ready to generate client artifacts. 🎉

```js {4} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: ['@hey-api/client-ofetch'], }; ``` ```sh {4} npx @hey-api/openapi-ts \ -i hey-api/backend \ -o src/client \ -c @hey-api/client-ofetch ```

Configuration

The ofetch client is built as a thin wrapper on top of ofetch, extending its functionality to work with Hey API. If you're already familiar with ofetch, configuring your client will feel like working directly with ofetch.

When we installed the client above, it created a client.gen.ts file. You will most likely want to configure the exported client instance. There are two ways to do that.

`setConfig()`

This is the simpler approach. You can call the setConfig() method at the beginning of your application or anytime you need to update the client configuration. You can pass any ofetch configuration option to setConfig(), and even your own ofetch instance.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  baseUrl: 'https://example.com',
});

The disadvantage of this approach is that your code may call the client instance before it's configured for the first time. Depending on your use case, you might need to use the second approach.

Runtime API

Since client.gen.ts is a generated file, we can't directly modify it. Instead, we can tell our configuration to use a custom file implementing the Runtime API. We do that by specifying the runtimeConfigPath option.

js {7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    {
      name: '@hey-api/client-ofetch',
      runtimeConfigPath: './src/hey-api.ts',
    },
  ],
};

In our custom file, we need to export a createClientConfig() method. This function is a simple wrapper allowing us to override configuration values.

ts title="src/hey-api.ts"
import type { CreateClientConfig } from './client/client.gen';

export const createClientConfig: CreateClientConfig = (config) => ({
  ...config,
  baseUrl: 'https://example.com',
});

With this approach, client.gen.ts will call createClientConfig() before initializing the client instance. If needed, you can still use setConfig() to update the client configuration later.

`createClient()`

You can also create your own client instance. You can use it to manually send requests or point it to a different domain.

js title="src/index.ts"
import { createClient } from './client/client';

const myClient = createClient({
  baseUrl: 'https://example.com',
});

You can also pass this instance to any SDK function through the client option. This will override the default instance from client.gen.ts.

js title="src/index.ts"
const response = await getFoo({
  client: myClient,
});

SDKs

Alternatively, you can pass the client configuration options to each SDK function. This is useful if you don't want to create a client instance for one-off use cases.

js title="src/index.ts"
const response = await getFoo({
  baseUrl: 'https://example.com', // <-- override default configuration
});

Interceptors

Interceptors (middleware) can be used to modify requests before they're sent or responses before they're returned to your application.

The ofetch client supports two complementary options:

  • built-in Hey API interceptors exposed via client.interceptors
  • native ofetch hooks passed through config (e.g., onRequest)

Example: Request interceptor

```js title="src/index.ts" import { client } from 'client/client.gen';

async function myInterceptor(request) {
// do something
return request;
}

interceptorId = client.interceptors.request.use(myInterceptor);

text
</TabItem>
  <TabItem label="eject">
```js title="src/index.ts"
import { client } from 'client/client.gen';

// eject by ID
client.interceptors.request.eject(interceptorId);

// eject by reference
client.interceptors.request.eject(myInterceptor);
```js title="src/index.ts" import { client } from 'client/client.gen';

async function myNewInterceptor(request) {
// do something
return request;
}

// update by ID
client.interceptors.request.update(interceptorId, myNewInterceptor);

// update by reference
client.interceptors.request.update(myInterceptor, myNewInterceptor);

text
</TabItem>
</Tabs>

### Example: Response interceptor

<Tabs>
  <TabItem label="use">
```js title="src/index.ts"
import { client } from 'client/client.gen';

async function myInterceptor(response) {
  // do something
  return response;
}

interceptorId = client.interceptors.response.use(myInterceptor);
```js title="src/index.ts" import { client } from 'client/client.gen';

// eject by ID
client.interceptors.response.eject(interceptorId);

// eject by reference
client.interceptors.response.eject(myInterceptor);

text
</TabItem>
  <TabItem label="update">
```js title="src/index.ts"
import { client } from 'client/client.gen';

async function myNewInterceptor(response) {
  // do something
  return response;
}

// update by ID
client.interceptors.response.update(interceptorId, myNewInterceptor);

// update by reference
client.interceptors.response.update(myInterceptor, myNewInterceptor);

:::tip
To eject, you must provide the ID or reference of the interceptor passed to use(), the ID is the value returned by use() and update().
:::

Example: `ofetch` hooks

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  onRequest: ({ options }) => {
    // mutate ofetch options (headers, query, etc.)
  },
  onResponse: ({ response }) => {
    // inspect/transform the raw Response
  },
  onRequestError: (ctx) => {
    // handle request errors
  },
  onResponseError: (ctx) => {
    // handle response errors
  },
});

Auth

The SDKs include auth mechanisms for every endpoint. You will want to configure the auth field to pass the right token for each request. The auth field can be a string or a function returning a string representing the token. The returned value will be attached only to requests that require auth.

js {4} title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  auth: () => '<my_token>',
  baseUrl: 'https://example.com',
});

If you're not using SDKs or generating auth, using interceptors is a common approach to configuring auth for each request.

js {4} title="src/index.ts"
import { client } from 'client/client.gen';

client.interceptors.request.use((request, options) => {
  request.headers.set('Authorization', 'Bearer <my_token>');
  return request;
});

You can also use the ofetch hooks.

js {5} title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  onRequest: ({ options }) => {
    options.headers.set('Authorization', 'Bearer <my_token>');
  },
});

Build URL

If you need to access the compiled URL, you can use the buildUrl() method. It's loosely typed by default to accept almost any value; in practice, you will want to pass a type hint.

ts title="src/index.ts"
type FooData = {
  path: {
    fooId: number;
  };
  query?: {
    bar?: string;
  };
  url: '/foo/{fooId}';
};

const url = client.buildUrl<FooData>({
  path: {
    fooId: 1,
  },
  query: {
    bar: 'baz',
  },
  url: '/foo/{fooId}',
});
console.log(url); // prints '/foo/1?bar=baz'

Custom Instance

You can provide a custom ofetch instance. This is useful if you need to extend the default instance with extra functionality, or replace it altogether.

js title="src/index.ts"
import { ofetch } from 'ofetch';
import { client } from 'client/client.gen';

const customOFetchInstance = ofetch.create({
  onRequest: ({ options }) => {
    // customize request
  },
  onResponse: ({ response }) => {
    // customize response
  },
});

client.setConfig({
  ofetch: customOFetchInstance,
});

You can use any of the approaches mentioned in Configuration, depending on how granular you want your custom instance to be.


Web Src Content Docs Docs Openapi Typescript Community Contributing (`web/src/content/docs/docs/openapi/typescript/community/contributing.mdx`)


title: Contributing sidebar: label: Overview

Contributor Manual

Foreword

Hey API is an ecosystem for turning API specifications into production-ready code. It's trusted by thousands of companies – from YC startups to Fortune 500 enterprises – and powers products used by millions worldwide.

We welcome contributors of all backgrounds and experience levels. Whether you're fixing a typo or building a new feature, your input matters. If you need guidance, help with technical writing, or want to bring a feature idea to life, we're here to support you.

:::tip
New to open source? Take a look at the Open Source or First Contributions guides for helpful information on contributing to open source projects.
:::

First Steps

There are many ways to contribute to Hey API. Most of them don't involve writing any code!

  • Read the documentation. Start with the Get Started guide. If you find anything broken or confusing, you can suggest improvements by clicking "Edit" at the bottom of any page.

  • Browse open issues. Help others by providing workarounds, asking for clarification, triaging, or suggesting labels on open issues. If you see something you would like to work on, consider opening a pull request.

  • Participate in discussions. Ask or answer questions, provide feedback, or suggest new ideas. Every idea is welcome, no matter how big or small.

  • Engage on social media. Help others discover Hey API by engaging with our posts on LinkedIn, Bluesky, or X. Share your experiences with Hey API on Reddit, Slack, or in your own communities and group chats.

  • Create a new issue. If you can't find a solution, open an issue. The issue template will guide you through the process.

  • Open a pull request. If you find an issue you would like to fix, open a pull request. If you need help, tag @mrlubos on GitHub, provide enough relevant information, and we will do our best to assist you.

These are some of the best ways not only to contribute to Hey API, but also to learn, connect with others, and share ideas.

Pull Requests

Ready to write some code? We have dedicated guides to help you build, develop, and test your feature before it's released.

We are excited to see what you'll contribute!


Web Src Content Docs Docs Openapi Typescript Community Spotlight (`web/src/content/docs/docs/openapi/typescript/community/spotlight.mdx`)


title: Spotlight

import { coreTeam, hallOfFame } from '@/data/people';
import Contributors from '@/components/Contributors.astro';
import TeamMembers from '@/components/TeamMembers.astro';

Spotlight

Meet the people behind Hey API. To join this list, please refer to the contributing guide.

Core Team

These people actively maintain Hey API.

Do you want to join the core team? Send us a short email describing your interest in Hey API, any relevant experience, and what you're hoping to work on.

Hall of Fame

These are the people with significant contributions to Hey API. A special thank you goes to Ferdi Koomen for allowing us to use the original source code from OpenAPI TypeScript Codegen. None of this would've been possible without you!

Contributors

The complete list of contributors to Hey API.

A sincere thank you for your contributions.


Web Src Content Docs Docs Openapi Typescript Configuration Input (`web/src/content/docs/docs/openapi/typescript/configuration/input.mdx`)


title: Input

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import InputTabs from '@/components/code-groups/InputTabs.mdx';

Input

You must provide an input so we can load your OpenAPI specification.

Input

The input can be a string path, URL, API registry, an object containing any of these, or an object representing an OpenAPI specification. Hey API supports all valid OpenAPI versions and file formats.

You can learn more about complex use cases in the Advanced section.

:::tip
If you use an HTTPS URL with a self-signed certificate in development, you will need to set NODE_TLS_REJECT_UNAUTHORIZED=0 in your environment.
:::

Request options

You can pass any valid Fetch API options to the request for fetching your specification. This is useful if your file is behind auth for example.

js {4-8} title="openapi-ts.config.ts"
export default {
  input: {
    path: 'https://secret.com/protected-spec',
    fetch: {
      headers: {
        Authorization: 'Bearer xxx',
      },
    },
  },
};

API Registry

You can store your specifications in an API registry to serve as a single source of truth. This helps prevent drift, improves discoverability, enables version tracking, and more.

Hey API

You can learn more about Hey API Platform on the Integrations page.

js {2} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
};

The input object lets you provide additional options to construct the correct URL.

js {4} title="openapi-ts.config.ts"
export default {
  input: {
    path: 'hey-api/backend', // sign up at app.heyapi.dev
    branch: 'main',
  },
};

We also provide shorthands for other registries:

ReadMe
Prefix your input with readme: to use the ReadMe API Registry.

```js {2} title="openapi-ts.config.ts" export default { input: 'readme:nysezql0wwo236', }; ``` ```js {2} title="openapi-ts.config.ts" export default { input: 'readme:@developers/v2.0#nysezql0wwo236', }; ```

Scalar
Prefix your input with scalar: to use the Scalar API Registry.

js {2} title="openapi-ts.config.ts"
export default {
  input: 'scalar:@scalar/access-service',
};

Watch Mode

:::caution
Watch mode currently supports only remote files via URL.
:::

If your schema changes frequently, you may want to automatically regenerate the output during development. To watch your input file for changes, enable input.watch mode in your configuration or pass the --watch flag to the CLI.

```js {4} title="openapi-ts.config.ts" export default { input: { path: 'hey-api/backend', // sign up at app.heyapi.dev watch: true, }, }; ``` ```sh "-w" npx @hey-api/openapi-ts -i hey-api/backend -o src/client -w ```

Web Src Content Docs Docs Openapi Typescript Configuration Output (`web/src/content/docs/docs/openapi/typescript/configuration/output.mdx`)


title: Output

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import OutputTabs from '@/components/code-groups/OutputTabs.mdx';

Output

You must set the output so we know where to generate your files.

Output

Output can be a path to the destination folder or an object containing the destination folder path and optional settings.

You can learn more about complex use cases in the Advanced section.

File

Control how files are named and annotated in the generated output.

File Name

You can customize the naming and casing pattern for files using the fileName option.

```js {4} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { fileName: '{{name}}', path: 'src/client', }, }; ``` ```js {5} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { fileName: { case: 'snake_case', }, path: 'src/client', }, }; ```

By default, we append every file name with a .gen suffix to highlight it's automatically generated. You can customize or disable this suffix using the fileName.suffix option.

```js {5} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { fileName: { suffix: '.gen', }, path: 'src/client', }, }; ``` ```js {5} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { fileName: { suffix: null, }, path: 'src/client', }, }; ``` ```js {5} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { fileName: { suffix: '.generated', }, path: 'src/client', }, }; ```

File Header

The generated output includes a notice in every file warning that any modifications will be lost when the files are regenerated. You can customize or disable this notice using the header option.

```js title="index.gen.ts" /* eslint-disable */ // This file is auto-generated by @hey-api/openapi-ts

/** ... */

text
</TabItem>
  <TabItem label="config">
```js {4-7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: {
    header: (ctx) => [
      '/* eslint-disable */',
      ...ctx.defaultValue,
    ],
    path: 'src/client',
  },
};

Module

Control how module specifiers are generated in the output.

Module Extension

Set module.extension to define the file extension used in import specifiers. This is useful when targeting environments that require fully specified imports (e.g., Node ESM or certain bundlers).

```js title="index.gen.ts" import foo from './foo.js'; import bar from './bar.js'; ``` ```js {5} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { module: { extension: '.js', }, path: 'src/client', }, }; ```

Module Path

Use module.resolve for full control over how module specifiers are generated. This lets you override specific modules or redirect them to custom locations (e.g., CDNs or internal aliases).

```js title="index.gen.ts" import * as z from 'https://esm.sh/zod'; ``` ```js {5-9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { module: { resolve(path) { if (path === 'zod') { return 'https://esm.sh/zod'; } }, }, path: 'src/client', }, }; ```

Source

Source is a copy of the input specification used to generate your output. It can be used to power documentation tools or to persist a stable snapshot alongside your generated files.

Enabling the source option with true creates a source.json file in your output folder.

js {5} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: {
    path: 'src/client',
    source: true,
  },
};

You can customize the file name and location using fileName and path. For example, this configuration will create an openapi.json file inside src/client/source directory.

js {6-7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: {
    path: 'src/client',
    source: {
      fileName: 'openapi',
      path: './source',
    },
  },
};

To use the source without writing it to disk, you can provide a callback function. This is useful for logging or integrating with external systems.

js {6-7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: {
    path: 'src/client',
    source: {
      callback: (source) => console.log(source),
      path: null,
    },
  },
};

Post Process

Post-processing allows you to run commands on the generated output folder after files are written. This is typically used to run formatters, linters, or other cleanup tools.

Commands are executed in order, and each command receives the output path via the path placeholder.

Presets

You can use built-in presets for common tools:

```js {5} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { path: 'src/client', postProcess: ['biome:check'], }, }; ``` ```js {5} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { path: 'src/client', postProcess: ['biome:format'], }, }; ``` ```js {5} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { path: 'src/client', postProcess: ['biome:lint'], }, }; ``` ```js {5} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { path: 'src/client', postProcess: ['eslint'], }, }; ``` ```js {5} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { path: 'src/client', postProcess: ['oxfmt'], }, }; ``` ```js {5} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { path: 'src/client', postProcess: ['oxlint'], }, }; ``` ```js {5} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { path: 'src/client', postProcess: ['prettier'], }, }; ```

Custom

You can also provide custom post processors:

js {5-10} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: {
    path: 'src/client',
    postProcess: [
      {
        command: 'dprint',
        args: ['fmt', '{{path}}'],
      },
    ],
  },
};

You can skip processing by adding the output path to the tool's ignore file (for example .eslintignore or .prettierignore).

Name Conflicts

As your project grows, the chances of name conflicts increase. We use a simple conflict resolver that appends numeric suffixes to duplicate identifiers. If you prefer a different strategy, you can provide your own nameConflictResolver function.

```js {4-6} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { nameConflictResolver({ attempt, baseName }) { return attempt === 0 ? baseName : `${baseName}_N${attempt + 1}`; }, path: 'src/client', }, }; ``` ```ts title="index.gen.ts" export type ChatCompletion = string;

export type ChatCompletion_N2 = number;

text
</TabItem>
</Tabs>

## TSConfig Path

We use the [TSConfig file](https://www.typescriptlang.org/tsconfig/) to generate output matching your project's settings. By default, we attempt to find a TSConfig file starting from the location of the `@hey-api/openapi-ts` configuration file and traversing up.

<Tabs>
  <TabItem label="default">
```js {5} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: {
    path: 'src/client',
    tsConfigPath: undefined,
  },
};
```js {5} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { path: 'src/client', tsConfigPath: './config/tsconfig.custom.json', }, }; ``` ```js {5} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: { path: 'src/client', tsConfigPath: null, }, }; ```

Custom Files

By default, you can't keep custom files in the path folder because it's emptied on every run. If you're sure you need to disable this behavior, set clean to false.

js {4} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: {
    clean: false,
    path: 'src/client',
  },
};

:::caution
Setting clean to false may result in broken output. Ensure you typecheck your code.
:::


Web Src Content Docs Docs Openapi Typescript Configuration Parser (`web/src/content/docs/docs/openapi/typescript/configuration/parser.mdx`)


title: Parser

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';

Parser

We parse your input before making it available to plugins. Configuring the parser is optional, but it provides an ideal opportunity to modify or validate your input as needed.

Patch

Sometimes you need to modify raw input before it's processed further. A common use case is fixing an invalid specification or adding a missing field. For this reason, custom patches are applied before any parsing takes place.

You can add custom patches with patch.

js {7-23} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  parser: {
    patch: {
      schemas: {
        Foo: (schema) => {
          // convert date-time format to timestamp
          delete schema.properties.updatedAt.format;
          schema.properties.updatedAt.type = 'number';
        },
        Bar: (schema) => {
          // add missing property
          schema.properties.meta = {
            additionalProperties: true,
            type: 'object',
          };
          schema.required = ['meta'];
        },
        Baz: (schema) => {
          // remove property
          delete schema.properties.internalField;
        },
      },
    },
  },
};

Validate

:::caution
The validator feature is very limited. You can help improve it by submitting more use cases.
:::

If you don't control or trust your input, you might want to validate it. Any detected errors in your input will exit @hey-api/openapi-ts and no plugins will be executed.

To validate your input, set validate_EXPERIMENTAL to true.

js {5} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  parser: {
    validate_EXPERIMENTAL: true,
  },
};

Filters

Filters allow you to trim your input before it's processed further, so your output contains only relevant resources.

Operations

Set include to match operations to be included or exclude to match operations to be excluded. Both exact keys and regular expressions are supported. When both rules match the same operation, exclude takes precedence over include.

```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { filters: { operations: { include: ['GET /api/v1/foo', '/^[A-Z]+ /api/v1//'], }, }, }, }; ``` ```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { filters: { operations: { exclude: ['GET /api/v1/foo', '/^[A-Z]+ /api/v1//'], }, }, }, }; ```

Tags

Set include to match tags to be included or exclude to match tags to be excluded. When both rules match the same tag, exclude takes precedence over include.

```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { filters: { tags: { include: ['v2'], }, }, }, }; ``` ```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { filters: { tags: { exclude: ['v1'], }, }, }, }; ```

Deprecated

You can filter out deprecated resources by setting deprecated to false.

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  parser: {
    filters: {
      deprecated: false,
    },
  },
};

Schemas

Set include to match schemas to be included or exclude to match schemas to be excluded. Both exact keys and regular expressions are supported. When both rules match the same schema, exclude takes precedence over include.

```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { filters: { schemas: { include: ['Foo', '/^Bar/'], }, }, }, }; ``` ```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { filters: { schemas: { exclude: ['Foo', '/^Bar/'], }, }, }, }; ```

Parameters

Set include to match parameters to be included or exclude to match parameters to be excluded. Both exact keys and regular expressions are supported. When both rules match the same parameter, exclude takes precedence over include.

```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { filters: { parameters: { include: ['QueryParameter', '/^MyQueryParameter/'], }, }, }, }; ``` ```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { filters: { parameters: { exclude: ['QueryParameter', '/^MyQueryParameter/'], }, }, }, }; ```

Request Bodies

Set include to match request bodies to be included or exclude to match request bodies to be excluded. Both exact keys and regular expressions are supported. When both rules match the same request body, exclude takes precedence over include.

```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { filters: { requestBodies: { include: ['Payload', '/^SpecialPayload/'], }, }, }, }; ``` ```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { filters: { requestBodies: { exclude: ['Payload', '/^SpecialPayload/'], }, }, }, }; ```

Responses

Set include to match responses to be included or exclude to match responses to be excluded. Both exact keys and regular expressions are supported. When both rules match the same response, exclude takes precedence over include.

```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { filters: { responses: { include: ['Foo', '/^Bar/'], }, }, }, }; ``` ```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { filters: { responses: { exclude: ['Foo', '/^Bar/'], }, }, }, }; ```

Orphaned resources

If you only want to exclude orphaned resources, set orphans to false. This is the default value when combined with any other filters. If this isn't the desired behavior, you may want to set orphans to true to always preserve unused resources.

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  parser: {
    filters: {
      orphans: false,
    },
  },
};

Order

For performance reasons, we don't preserve the original order when filtering out resources. If maintaining the original order is important to you, set preserveOrder to true.

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  parser: {
    filters: {
      preserveOrder: true,
    },
  },
};

Transforms

You can think of transforms as deterministic patches. They provide an easy way to apply the most commonly used input transformations.

Enums

Your input might contain two types of enums:

  • enums defined as reusable components (root enums)
  • non-reusable enums nested within other schemas (inline enums)

You may want all enums to be reusable. This is because only root enums are typically exported by plugins. Inline enums will never be directly importable since they're nested inside other schemas.

```js {6} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { transforms: { enums: 'root', }, }, }; ``` ```js {6} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { transforms: { enums: 'inline', }, }, }; ```

You can customize the naming and casing pattern for enums schemas using the .name and .case options.

Properties required by default

By default, any object schema with a missing required keyword is interpreted as "no properties are required." This is the correct behavior according to the OpenAPI standard. However, some specifications interpret a missing required keyword as "all properties should be required."

This option allows you to change the default behavior so that properties are required by default unless explicitly marked as optional.

```js {6} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { transforms: { propertiesRequiredByDefault: false, }, }, }; ``` ```js {6} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { transforms: { propertiesRequiredByDefault: true, }, }, }; ```

Read-write

Your schemas might contain read-only or write-only fields. Using such schemas directly could mean asking the user to provide a read-only field in requests, or expecting a write-only field in responses. We separate schemas for requests and responses if direct usage would result in such scenarios.

```js {7-8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { transforms: { readWrite: { requests: '{{name}}Writable', responses: '{{name}}', }, }, }, }; ``` ```js {6} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { transforms: { readWrite: false, }, }, }; ```

You can customize the naming and casing pattern for requests and responses schemas using the .name and .case options.

Schema Name

Sometimes your schema names are auto-generated or follow a naming convention that produces verbose or awkward type names. You can rename schema component keys throughout the specification, automatically updating all $ref pointers. For example, stripping version markers from schema names, removing vendor prefixes, converting naming conventions, or shortening deeply qualified names.

```js {6-16} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { transforms: { schemaName: (name) => { // Strip version markers: ServiceRoot_v1_20_0_ServiceRoot → ServiceRoot let clean = name.replace( /([A-Za-z\d]+)_v\d+_\d+_\d+_([A-Za-z\d]*)/g, (_, p1, p2) => (p2.startsWith(p1) ? p2 : p1 + p2), ); // Deduplicate prefixes: Foo_Foo → Foo const m = clean.match(/^([A-Za-z\d]+)_\1([A-Za-z\d]*)$/); if (m) clean = m[1] + m[2]; return clean; }, }, }, }; ``` ```js {6} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { transforms: { schemaName: 'Api{{name}}', }, }, }; ```

:::note[Name Collisions]
If a transformed schema name conflicts with an existing schema, the rename is skipped for that schema to prevent overwrites. The original name is preserved.
:::

Pagination

Paginated operations are detected by having a pagination keyword in its parameters or request body. By default, we consider the following to be pagination keywords: after, before, cursor, offset, page, and start.

You can provide custom pagination keywords using pagination.keywords.

```js {9-12} title="openapi-ts.config.ts" import { defaultPaginationKeywords } from '@hey-api/openapi-ts';

export default {
input: 'hey-api/backend', // sign up at app.heyapi.dev
output: 'src/client',
parser: {
pagination: {
keywords: [
...defaultPaginationKeywords,
'extra',
'pagination',
'keywords',
],
},
},
};

text
</TabItem>
  <TabItem label="override">
```js {7-9} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  parser: {
    pagination: {
      keywords: [
        'custom',
        'pagination',
        'keywords',
      ],
    },
  },
};

Hooks

Hooks affect runtime behavior but aren't tied to any single plugin. They can be configured globally via hooks or per plugin through the $hooks property.

```js {5} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { hooks: {}, // configure global hooks }, }; ``` ```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ { name: '@tanstack/react-query', $hooks: {}, // configure plugin hooks }, ], }; ```

We always use the first hook that returns a value. If a hook returns no value, we fall back to less specific hooks until one does.

Operations

Each operation has a list of classifiers that can include query, mutation, both, or none. Plugins may use these values to decide whether to generate specific output. For example, you usually don't want to generate TanStack Query options for PATCH operations.

Query operations

By default, GET operations are classified as query operations.

Mutation operations

By default, DELETE, PATCH, POST, and PUT operations are classified as mutation operations.

Example: POST search query

Imagine your API has a POST /search endpoint that accepts a large payload. By default, it's classified as a mutation, but in practice it behaves like a query, and your state management plugin should generate query hooks.

You can achieve this by classifying the operation as query in a matcher.

```js {8-10} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { hooks: { operations: { isQuery: (op) => { if (op.method === 'post' && op.path === '/search') { return true; } }, }, }, }, }; ``` ```js {8-10} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', parser: { hooks: { operations: { getKind: (op) => { if (op.method === 'post' && op.path === '/search') { return ['query']; } }, }, }, }, }; ```

Symbols

Each symbol can have a placement function deciding its output location.

Example: Alphabetic sort

While we work on a better example, let's imagine a world where it's desirable to place every symbol in a file named after its initial letter. For example, a function named Foo should end up in the file f.ts.

You can achieve this by using the symbol's name.

js {7-11} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  parser: {
    hooks: {
      symbols: {
        getFilePath: (symbol) => {
          if (symbol.name) {
            return symbol.name[0]?.toLowerCase();
          }
        },
      },
    },
  },
};

Most plugins expose configuration options that allow you to rename many of the generated symbols. If you need even more control, use the getName() hook.

Example: Enum naming

By default, generated enums use the same name for both the type and the runtime value.

```ts title="index.gen.ts" export const Flags = { ALPHA: 'alpha', BETA: 'beta', } as const;

export type Flags = (typeof Flags)[keyof typeof Flags];

text
</TabItem>
  <TabItem label="config">
```js title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    {
      enums: 'javascript',
      name: '@hey-api/typescript',
    },
  ],
};

While this code works perfectly fine due to TypeScript's declaration merging, let's say we want to use a different name for the type. We can accomplish this with the getName() hook.

```ts title="index.gen.ts" "FlagsType" export const Flags = { ALPHA: 'alpha', BETA: 'beta', } as const;

export type FlagsType = (typeof Flags)[keyof typeof Flags];

text
</TabItem>
  <TabItem label="config">
```js {10-14} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    {
      enums: 'javascript',
      name: '@hey-api/typescript',
      $hooks: {
        symbols: {
          getName({ meta, name, schema }) {
            if (schema?.type === 'enum' && meta.category === 'type') {
              return `${name}Type`;
            }
          },
        },
      },
    },
  ],
};

Web Src Content Docs Docs Openapi Typescript Configuration Vite (`web/src/content/docs/docs/openapi/typescript/configuration/vite.mdx`)


title: Vite Plugin sidebar: label: Vite

import Examples from '@/components/Examples.astro';
import InstallCommand from '@/components/package-managers/InstallCommand.astro';

Vite

About

Vite is a blazing fast frontend build tool powering the next generation of web applications.

The Vite plugin integrates @hey-api/openapi-ts into the Vite build pipeline, running automatically whenever Vite resolves its configuration – no separate script or manual step required.

Features

  • runs automatically as part of your Vite build
  • reads your existing configuration (or accepts inline config)
  • supports Vite 5, 6, 7, and 8

Installation

You can download @hey-api/vite-plugin from npm using your favorite package manager.

Usage

Add the plugin to your vite.config.ts:

js title="vite.config.ts"
import { heyApiPlugin } from '@hey-api/vite-plugin';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [heyApiPlugin()],
});

The plugin will automatically pick up your configuration file. You can also pass options inline using the config option:

js title="vite.config.ts"
import { heyApiPlugin } from '@hey-api/vite-plugin';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    heyApiPlugin({
      config: {
        input: 'hey-api/backend', // sign up at app.heyapi.dev
        output: 'src/client',
      },
    }),
  ],
});

Vite Options

You can pass Vite Plugin API options using the vite option. For example, to run the plugin only during development:

js title="vite.config.ts"
import { heyApiPlugin } from '@hey-api/vite-plugin';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    heyApiPlugin({
      config: {
        input: 'hey-api/backend', // sign up at app.heyapi.dev
        output: 'src/client',
      },
      vite: {
        apply: 'serve',
      },
    }),
  ],
});

Web Src Content Docs Docs Openapi Typescript Plugins Adonis (`web/src/content/docs/docs/openapi/typescript/plugins/adonis.mdx`)


title: AdonisJS sidebar: label: Adonis

import FeatureStatus from '@/components/FeatureStatus.astro';

AdonisJS

About

AdonisJS is a TypeScript-first web framework for building web apps and API servers. It comes with support for testing, modern tooling, an ecosystem of official packages, and more.


Web Src Content Docs Docs Openapi Typescript Plugins Ajv (`web/src/content/docs/docs/openapi/typescript/plugins/ajv.mdx`)


title: Ajv

import FeatureStatus from '@/components/FeatureStatus.astro';

Ajv

About

Ajv is the fastest JSON validator for Node.js and browser.


Web Src Content Docs Docs Openapi Typescript Plugins Arktype (`web/src/content/docs/docs/openapi/typescript/plugins/arktype.mdx`)


title: Arktype

import FeatureStatus from '@/components/FeatureStatus.astro';

Arktype

About

Arktype is a TypeScript's 1:1 validator, optimized from editor to runtime.


Web Src Content Docs Docs Openapi Typescript Plugins Chance (`web/src/content/docs/docs/openapi/typescript/plugins/chance.mdx`)


title: Chance

import FeatureStatus from '@/components/FeatureStatus.astro';

Chance

About

Chance is a minimalist generator of random strings, numbers, etc. to help reduce some monotony particularly while writing automated tests or anywhere else you need anything random.


Web Src Content Docs Docs Openapi Typescript Plugins Custom (`web/src/content/docs/docs/openapi/typescript/plugins/custom.mdx`)


title: Custom Plugin sidebar: label: Plugin

import { FileTree, Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';

:::caution
Plugin API is in development. The interface might change before it becomes stable. We encourage you to leave feedback on GitHub.
:::

You may need to write your own plugin if the available plugins do not suit your needs or you're working on a proprietary use case. This can be easily achieved using the Plugin API. But don't take our word for it – all Hey API plugins are written this way!

File Structure

We recommend following the design pattern of the native plugins. You can browse the code on GitHub as you follow this tutorial.

- my-plugin/ - config.ts - imports.ts - index.ts - plugin.ts - types.ts

First, create a my-plugin folder for your plugin files. Inside, create a barrel file index.ts exporting the plugin.

ts title="index.ts"
export { defaultConfig, defineConfig } from './config';
export type { MyPlugin } from './types';

Types

index.ts references two files, so we need to create them. types.ts contains the TypeScript interface for your plugin options.

ts title="types.ts"
import type { DefinePlugin, Plugin } from '@hey-api/openapi-ts';

export type UserConfig = Plugin.Name<'my-plugin'> &
  Plugin.Hooks &
  Plugin.UserExports & {
    /**
     * User-configurable option for your plugin.
     */
    user?: number | string | {
      age?: number;
      name?: string;
    };
  };

export type Config = Plugin.Name<'my-plugin'> &
  Plugin.Hooks &
  Plugin.Exports & {
    /** Resolved option for your plugin. */
    user: {
      age: number;
      name: string;
    };
  };

export type MyPlugin = DefinePlugin<UserConfig, Config>;
  • Plugin.Name sets the plugin's unique identifier.
  • Plugin.Hooks allows users to override plugin behavior through event hooks. For example, users can control how schemas are extracted or how symbols are routed to files.
  • Plugin.Exports add the includeInEntry option, which controls whether your plugin's exports are re-exported from the output entry file.
  • UserConfig is what users write in their config file. Config is the fully resolved shape your handler receives.

Configuration

config.ts defines the runtime configuration for your plugin, including how user input is resolved into Config and the handler function that generates output.

ts title="config.ts"
import { definePluginConfig } from '@hey-api/openapi-ts';

import { handler } from './plugin';
import type { MyPlugin } from './types';

export const defaultConfig: MyPlugin['Config'] = {
  config: {
    user: {
      age: 42,
      name: 'Stan Smith',
    },
  },
  handler,
  name: 'my-plugin',
};

/**
 * Type helper for `my-plugin` plugin, returns {@link Plugin.Config} object
 */
export const defineConfig = definePluginConfig(defaultConfig);

The config table is a declarative resolution spec. Each field declares its default value and optional coercion rules. To allow users to set either age or name through the user field, we can add coercion rules to transform primitive values into the expected shape.

```ts {9-12} title="config.ts" import { definePluginConfig } from '@hey-api/openapi-ts';

import { handler } from './plugin';
import type { MyPlugin } from './types';

export const defaultConfig: MyPlugin['Config'] = {
config: {
user: {
$coerce: {
number: (v) => ({ age: v }),
string: (v) => ({ name: v }),
},
age: 42,
name: 'Stan Smith',
},
},
handler,
name: 'my-plugin',
};

/**

  • Type helper for my-plugin plugin, returns {@link Plugin.Config} object
    */
    export const defineConfig = definePluginConfig(defaultConfig);
text
</TabItem>
  <TabItem label="input">
```js {8} title="openapi-ts.config.ts"
import { defineConfig } from 'path/to/my-plugin';

export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    defineConfig({
      user: 'Joe Doe', // or number to set age
    }),
  ],
};
```ts { user: { age: 42, name: 'Joe Doe' } } ```

Dependencies

Plugins can declare dependencies on other plugins. A dependency will always be included in the output, even if the user hasn't explicitly added it to their plugins config. This is useful when your plugin's output builds on another plugin's types or symbols.

ts {3} title="config.ts"
export const defaultConfig: MyPlugin['Config'] = {
  config: { ... },
  dependencies: ['@hey-api/typescript'],
  handler,
  name: 'my-plugin',
};

Resolving tags

Some dependencies aren't known at authoring time, such as which validator plugin the user has installed. Use resolveTag inside a coerce() call to look up the active plugin for a given tag at resolution time. In most cases, you'll also want to declare a dependency on the field to ensure it's included in the output.

ts {1,2,6,9} title="config.ts"
import type { PluginContext } from '@hey-api/openapi-ts';
import { coerce } from '@hey-api/openapi-ts';

export const defaultConfig: MyPlugin['Config'] = {
  config: {
    $dependencies: ['validator'],
    validator: coerce((value, context) => {
      if (value === true || value === undefined) {
        return (context as PluginContext).resolveTag('validator');
      }
      return value;
    }),
  },
  dependencies: ['@hey-api/typescript'],
  handler,
  name: 'my-plugin',
};

Handler

The handler function generates the actual output. We recommend implementing it in plugin.ts.

ts title="plugin.ts"
import { $ } from '@hey-api/openapi-ts';

import type { MyPlugin } from './types';

export const handler: MyPlugin['Handler'] = ({ plugin }) => {
  plugin.forEach('operation', 'schema', (event) => {
    if (event.type === 'operation') {
      // do something with the operation
    } else if (event.type === 'schema') {
      // do something with the schema
    }
  });

  // use the TypeScript DSL to build nodes
  const symbolName = plugin.symbol('user');
  const node = $.const(symbolName)
    .export()
    .assign($.literal(plugin.config.user.name));
  plugin.node(node);
};

Imports

Imports are a type-safe way to reference external symbols your plugin works with. Rather than querying or constructing identifiers manually, you declare them once and access them through plugin.imports anywhere in your handler.

Imports are also visible to users customizing your plugin's behavior. When a user provides hooks like $resolvers, they receive the plugin instance and can access plugin.imports to reference your plugin's imports directly.

```ts title="imports.ts" import type { PluginInstance } from '@hey-api/openapi-ts';

export function myPluginImports(plugin: PluginInstance) {
const factory = plugin.symbolFactory;
return {
myLib: factory.register('myLib', { external: 'my-lib' }),
};
}

export type MyPluginImports = ReturnType;

text
</TabItem>
  <TabItem label="types">
```ts {1,5} title="types.ts"
import type { MyPluginImports } from './imports';

/** ... */

export type MyPlugin = DefinePlugin<UserConfig, Config, never, MyPluginImports>;
```ts {1,6} title="config.ts" import { myPluginImports } from './imports';

export const defaultConfig: MyPlugin['Config'] = {
config: { ... },
handler,
imports: myPluginImports,
name: 'my-plugin',
};

text
</TabItem>
  <TabItem label="handler">
```ts {2} title="plugin.ts"
export const handler: MyPlugin['Handler'] = ({ plugin }) => {
  const { myLib } = plugin.imports;
  // use myLib as a typed reference when building nodes
};

Usage

Once you're satisfied with your plugin, register it in the configuration file.

js {1,7} title="openapi-ts.config.ts"
import { defineConfig } from 'path/to/my-plugin';

export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    defineConfig(),
  ],
};

Output

Putting all of this together will generate the following my-plugin.gen.ts file.

ts title="my-plugin.gen.ts"
export const user = 'Stan Smith';

Congratulations! You've successfully created your own plugin! 🎉


Web Src Content Docs Docs Openapi Typescript Plugins Elysia (`web/src/content/docs/docs/openapi/typescript/plugins/elysia.mdx`)


title: Elysia

import FeatureStatus from '@/components/FeatureStatus.astro';

Elysia

About

Elysia is an ergonomic framework for humans. TypeScript with end-to-end type safety, type integrity, and exceptional developer experience. Supercharged by Bun.


Web Src Content Docs Docs Openapi Typescript Plugins Express (`web/src/content/docs/docs/openapi/typescript/plugins/express.mdx`)


title: Express

import FeatureStatus from '@/components/FeatureStatus.astro';

Express

About

Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.


Web Src Content Docs Docs Openapi Typescript Plugins Faker (`web/src/content/docs/docs/openapi/typescript/plugins/faker.mdx`)


title: Faker v10 Plugin sidebar: label: Faker custom: version: v10

import { Tabs, TabItem } from '@astrojs/starlight/components';
import AuthorsList from '@/components/AuthorsList.astro';
import Examples from '@/components/Examples.astro';
import FeatureStatus from '@/components/FeatureStatus.astro';
import UserConfig from '@/components/UserConfig.astro';
import { malcolmKee } from '@/data/people';

Faker

About

Faker is a popular library that generates fake (but reasonable) data that can be used for things such as unit testing, performance testing, building demos, and working without a completed backend.

The Faker plugin for Hey API generates factory functions from your OpenAPI spec that return realistic mock data using @faker-js/faker.

Collaborators

Features

  • Faker v9 and v10 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • factory functions for reusable schema definitions, operation requests, and operation responses
  • smart property name inference for realistic data (e.g. email → faker.internet.email())
  • constraint and format awareness (min/max, string formats, array bounds)
  • optional property and default value handling
  • dependency injection for custom faker instances (locale, seed)
  • minimal learning curve thanks to extending the underlying technology

Installation

In your configuration, add @faker-js/faker to your plugins and you'll be ready to generate faker factories. 🎉

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    '@faker-js/faker',
  ],
};

Output

The Faker plugin will generate the following artifacts, depending on the input specification.

Definitions

A factory function is generated for every reusable definition from your input.

```ts title="faker.gen.ts" import { faker, type Faker } from '@faker-js/faker';

import type { Bar, Foo } from '../types.gen';

export const fakeFoo = (options?: Options): Foo => ({
name: ensureFaker(options).string.sample(),
age: ensureFaker(options).number.int({ min: 1, max: 120 }),
});

export const fakeBar = (options?: Options): Bar =>
ensureFaker(options).helpers.arrayElement(['baz', 'qux']);

text
</TabItem>
  <TabItem label="config">
```js {8} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@faker-js/faker',
      definitions: true,
    },
  ],
};

You can customize the naming and casing pattern for definitions factories using the .name and .case options.

Requests

A single factory function is generated per operation for request data. It combines the request body, path parameters, query parameters, and headers into one object. Only keys with actual data are included.

```ts title="faker.gen.ts" // body + path + query combined into one factory export const fakeUpdatePetRequest = (options?: Options): Omit => { const f = options?.faker ?? faker; return { body: { name: f.string.sample(), tag: fakeTag(options), }, path: { id: f.string.uuid(), }, query: { dryRun: f.datatype.boolean(), }, }; };

// body only
export const fakeCreatePetRequest = (options?: Options): Omit<CreatePetData, 'url'> => {
const f = options?.faker ?? faker;
return {
body: {
name: f.string.sample(),
},
};
};

text
</TabItem>
  <TabItem label="config">
```js {8} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@faker-js/faker',
      requests: true,
    },
  ],
};

You can customize the naming and casing pattern for requests factories using the .name and .case options.

Responses

A factory function is generated for operation responses. When an operation has a single success response, the factory is unsuffixed. When multiple responses exist, each factory is suffixed with the status code.

```ts title="faker.gen.ts" // single success response — unsuffixed export const fakeCreatePetResponse = (options?: Options): CreatePetResponse => fakePet(options);

// multiple responses — suffixed with status code
export const fakeGetPetResponse200 = (options?: Options): GetPetResponses[200] => fakePet(options);

export const fakeGetPetResponse404 = (options?: Options): GetPetErrors[404] => fakeError(options);

text
</TabItem>
  <TabItem label="config">
```js {8} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@faker-js/faker',
      responses: true,
    },
  ],
};

You can customize the naming and casing pattern for responses factories using the .name and .case options.

Smart Inference

The plugin infers semantically appropriate faker methods from property names, producing more realistic mock data without requiring explicit schema formats or annotations.

ts
// property name "email" → faker.internet.email()
// property name "firstName" → faker.person.firstName()
// property name "city" → faker.location.city()
// property name "id" → faker.string.uuid()
// property name "age" → faker.number.int({ min: 1, max: 120 })

Ancestor context is also used for disambiguation. For example, name under a User schema produces faker.person.fullName(), while name under a Company schema produces faker.company.name().

Explicit schema annotations (format, pattern, constraints) always take priority over name-based inference.

Name Rules

You can extend or override the built-in name inference with custom nameRules. Rules are defined per schema type (string or number) and map property names to specific faker methods.

```ts title="faker.gen.ts" export const fakeError = (options?: Options): Error => { const f = options?.faker ?? faker; return { code: f.number.int(), message: f.word.words({ count: 4 }), }; };

export const fakeDocument = (options?: Options): Document => {
const f = options?.faker ?? faker;
return {
id: f.database.mongodbObjectId(),
_id: f.database.mongodbObjectId(),
};
};

text
</TabItem>
  <TabItem label="config">
```js {8-21} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@faker-js/faker',
      nameRules: {
        string: {
          // compound rule: "message" inside an "error" schema
          'error.message': {
            fakerPath: ['word', 'words'],
            defaultArgs: { count: 4 },
          },
          // suffix rule: any property ending with "id"
          id: {
            fakerPath: ['database', 'mongodbObjectId'],
            suffixMatch: true,
          },
        },
      },
    },
  ],
};

Each rule requires a fakerPath — a tuple of [module, method] corresponding to a faker method (e.g. ['database', 'mongodbObjectId'] for faker.database.mongodbObjectId()). You can optionally provide defaultArgs to pass default arguments to the faker method.

Matching strategies:

  • Exact match — 'email' matches a property named email
  • Compound match — 'error.message' matches a property named message inside an ancestor named error
  • Suffix match — 'id' with suffixMatch: true matches any property ending with id (e.g. userId, _id)

User-provided rules take priority over built-in rules. Property names are normalized (lowercased, separators removed) before matching.

Formats and Constraints

The plugin respects OpenAPI schema formats and constraints to generate appropriately bounded data.

String formats:

  • email → faker.internet.email()
  • date-time → faker.date.recent().toISOString()
  • date → faker.date.recent().toISOString().slice(0, 10)
  • uuid → faker.string.uuid()
  • uri / url → faker.internet.url()
  • ipv4 → faker.internet.ipv4()
  • ipv6 → faker.internet.ipv6()
  • pattern → faker.helpers.fromRegExp(pattern)

Numeric constraints:

  • minimum / maximum → faker.number.int({ min, max })
  • exclusiveMinimum / exclusiveMaximum → adjusted bounds

String constraints:

  • minLength / maxLength → faker.string.alpha({ length: { min, max } })

Array constraints:

  • minItems / maxItems → controls count in faker.helpers.multiple()

Optional Properties

Required properties are always included. Optional properties are controlled by the includeOptional option at runtime.

ts
const user = fakeFoo(); // includes optional properties by default
const user = fakeFoo({ includeOptional: false }); // excludes optional properties
const user = fakeFoo({ includeOptional: 0.5 }); // 50% probability per optional property

Default Values

When a schema defines default values, you can control whether to use them instead of generating fake data via the useDefault option.

ts
const data = fakeFoo(); // always generates fake data
const data = fakeFoo({ useDefault: true }); // uses schema defaults when available
const data = fakeFoo({ useDefault: 0.5 }); // 50% probability of using defaults

Circular References

Schemas with circular references are automatically detected, whether self-referencing (a schema that refers to itself) or mutually recursive (two or more schemas that refer to each other). The generated factories use a depth counter to prevent infinite recursion, returning empty arrays or omitting optional properties once the limit is reached.

The maximum depth defaults to 10 and can be configured via the maxCallDepth option.

Locale

You can configure the locale for generated faker factories. When set, the generated code imports the faker instance from the locale-specific build of @faker-js/faker.

```ts title="faker.gen.ts" import { faker } from '@faker-js/faker/locale/de'; import type { Faker } from '@faker-js/faker'; ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@faker-js/faker', locale: 'de', }, ], }; ```

See the Faker localization guide for available locales.

Deterministic Results

For snapshot testing or reproducible output, you can seed the faker instance and fix the reference date to ensure deterministic results.

ts
import { faker } from '@faker-js/faker';

faker.seed(42);
faker.setDefaultRefDate('2026-01-01T00:00:00.000Z');

const data = fakeFoo();
// always returns the same output

Custom Faker Instance

You can pass your own faker instance via the faker option for runtime locale switching or other customizations.

ts
import { faker } from '@faker-js/faker/locale/de';

const data = fakeFoo({ faker });

Web Src Content Docs Docs Openapi Typescript Plugins Falso (`web/src/content/docs/docs/openapi/typescript/plugins/falso.mdx`)


title: Falso

import FeatureStatus from '@/components/FeatureStatus.astro';

Falso

About

Falso creates massive amounts of fake data in the browser and NodeJS. Tree shakeable & fully typed.


Web Src Content Docs Docs Openapi Typescript Plugins Fastify (`web/src/content/docs/docs/openapi/typescript/plugins/fastify.mdx`)


title: Fastify v5 Plugin sidebar: label: Fastify custom: version: v5

import { Tabs, TabItem } from '@astrojs/starlight/components';
import AuthorsList from '@/components/AuthorsList.astro';
import Examples from '@/components/Examples.astro';
import FeatureStatus from '@/components/FeatureStatus.astro';
import UserConfig from '@/components/UserConfig.astro';
import { jacobCohen } from '@/data/people';

Fastify

About

Fastify is a fast and low overhead web framework for Node.js.

The Fastify plugin for Hey API generates route handlers from your OpenAPI spec, fully compatible with all core features.

Collaborators

Features

  • Fastify v5 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • type-safe route handlers
  • minimal learning curve thanks to extending the underlying technology

Installation

In your configuration, add fastify to your plugins and you'll be ready to generate Fastify artifacts. 🎉

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    'fastify',
  ],
};

Output

The Fastify plugin will generate the following artifacts, depending on the input specification.

Route Handlers

Route handlers are generated from all endpoints. The generated interface follows the naming convention of SDK functions.

```ts title="fastify.gen.ts" const fastify = Fastify(); const serviceHandlers: RouteHandlers = { createPets(request, reply) { reply.code(201).send(); }, listPets(request, reply) { reply.code(200).send([]); }, showPetById(request, reply) { reply.code(200).send({ id: Number(request.params.petId), name: 'Kitty', }); }, }; fastify.register(glue, { serviceHandlers }); ``` ```js title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins 'fastify', ], }; ```

Web Src Content Docs Docs Openapi Typescript Plugins Hono (`web/src/content/docs/docs/openapi/typescript/plugins/hono.mdx`)


title: Hono

import FeatureStatus from '@/components/FeatureStatus.astro';

Hono

About

Hono is a small, simple, and ultrafast web framework built on Web Standards. It works on any JavaScript runtime: Cloudflare Workers, Fastly Compute, Deno, Bun, Vercel, Netlify, AWS Lambda, Lambda@Edge, and Node.js.


Web Src Content Docs Docs Openapi Typescript Plugins Joi (`web/src/content/docs/docs/openapi/typescript/plugins/joi.mdx`)


title: Joi

import FeatureStatus from '@/components/FeatureStatus.astro';

Joi

About

Joi is the most powerful schema description language and data validator for JavaScript.


Web Src Content Docs Docs Openapi Typescript Plugins Koa (`web/src/content/docs/docs/openapi/typescript/plugins/koa.mdx`)


title: Koa

import FeatureStatus from '@/components/FeatureStatus.astro';

Koa

About

Koa is a new web framework designed by the team behind Express, which aims to be a smaller, more expressive, and more robust foundation for web applications and APIs.


Web Src Content Docs Docs Openapi Typescript Plugins Msw (`web/src/content/docs/docs/openapi/typescript/plugins/msw.mdx`)


title: MSW v2 Plugin sidebar: label: MSW custom: version: v2

import AuthorsList from '@/components/AuthorsList.astro';
import Examples from '@/components/Examples.astro';
import FeatureStatus from '@/components/FeatureStatus.astro';
import UserConfig from '@/components/UserConfig.astro';
import { malcolmKee } from '@/data/people';

MSW

About

MSW is an API mocking library that allows you to write client-agnostic mocks and reuse them across any frameworks, tools, and environments.

The MSW plugin for Hey API generates type-safe mock handler factories from your OpenAPI spec, fully compatible with SDKs, transformers, and all core features.

Collaborators

Features

  • MSW v2 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • type-safe mock handlers
  • minimal learning curve thanks to extending the underlying technology

Installation

In your configuration, add msw to your plugins and you'll be ready to generate MSW artifacts. 🎉

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    'msw',
  ],
};

Output

The MSW plugin will generate the following artifacts, depending on the input specification.

Handler Exports

Each operation is exported as a handler creator named <operationId>Mock. These use a wildcard (*) base URL so they match requests regardless of origin. A getAllMocks function is also exported.

ts
import { getPetByIdMock, getAllMocks } from './client/msw.gen';

Usage

Static Response

The simplest way to mock an endpoint is to provide a static response object with a result property. The status property is optional — when omitted, it defaults to the operation's dominant success status code (e.g. 200).

ts
import { setupServer } from 'msw/node';
import { getPetByIdMock } from './client/msw.gen';

const mockServer = setupServer(
  // result only — status defaults to the dominant success code (200)
  getPetByIdMock({ result: { id: 1, name: 'Fido' } }),

  // explicit status code
  getPetByIdMock({ status: 200, result: { id: 1, name: 'Fido' } }),

  // type error if result type is incorrect
  // @ts-expect-error
  getPetByIdMock({ result: 'wrong type' }),
);

Custom Resolver

For more control, pass an MSW HttpResponseResolver function. The resolver receives typed path parameters and request body when available.

ts
import { delay, HttpResponse } from 'msw';
import { setupServer } from 'msw/node';
import { getPetByIdMock, updatePetMock } from './client/msw.gen';

const mockServer = setupServer(
  // custom resolver with typed params and body
  updatePetMock(async ({ request, params }) => {
    const body = await request.json();
    return HttpResponse.json({ id: Number(params.petId), ...body }, { status: 200 });
  }),

  // async resolver with delay
  getPetByIdMock(async () => {
    await delay(100);
    return HttpResponse.json({ id: 1, name: 'Fido' });
  }),
);

:::note
Path parameters are typed as string because MSW normalizes all path parameters to strings. Use Number() or similar conversions if you need numeric values.
:::

Operations Without Responses

For operations that don't define a response type, the handler creator can be invoked without arguments or with a custom resolver function.

ts
import { deletePetMock } from './client/msw.gen';

const mockServer = setupServer(
  deletePetMock(),

  deletePetMock(() => new HttpResponse(null, { status: 204 })),
);

Response Examples

When your OpenAPI spec includes response examples, the generated handlers will use them as default values. This means you can call the handler without arguments and it will return the example response automatically.

ts
import { getFooMock } from './client/msw.gen';

const mockServer = setupServer(
  // uses the example response from the OpenAPI spec as default
  getFooMock(),

  // you can still override with a custom response
  getFooMock({ result: { name: 'Custom' } }),
);

By default, source is set to ['@hey-api/examples'], which embeds OpenAPI examples in the generated output. To disable this, set source to an empty array.

js {8} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'msw',
      source: [],
    },
  ],
};

All Handlers (`getAllMocks`)

The getAllMocks function generates handlers for all operations at once. This is useful for quickly setting up a mock server without manually listing each operation.

ts
import { setupServer } from 'msw/node';
import { getAllMocks } from './client/msw.gen';

const server = setupServer(...getAllMocks());

`onMissingMock`

Some operations require a response argument (because they have no default example value). The onMissingMock option controls what happens for these operations:

  • 'skip' (default) — skips handlers that require an argument, only including operations that have default values or no response type
  • 'error' — includes all handlers, but operations without a provided response return a 501 error with the message '[heyapi-msw] The mock of this request is not implemented.'
ts
// strict mode: all endpoints are mocked, missing ones return 501
const server = setupServer(...getAllMocks({ onMissingMock: 'error' }));

`overrides`

Use overrides to provide static responses for specific operations. The keys are handler names (operation ID suffixed with Mock) and the values are the same as what you'd pass to the individual handler creator.

ts
import { setupServer } from 'msw/node';
import { getAllMocks } from './client/msw.gen';

const server = setupServer(
  ...getAllMocks({
    onMissingMock: 'skip',
    overrides: {
      getPetByIdMock: {
        result: { id: 1, name: 'Fido', photoUrls: [] },
      },
    },
  }),
);

When an override is provided for a required handler, it will always be included regardless of the onMissingMock setting.

Custom Base URL

The individually exported handlers use a wildcard (*) base URL that matches requests regardless of origin. If you need handlers bound to a specific base URL, use the createMswHandlerFactory function:

ts
import { setupServer } from 'msw/node';
import { createMswHandlerFactory } from './client/msw.gen';

const createMock = createMswHandlerFactory({
  baseUrl: 'http://localhost:8000',
});

const server = setupServer(createMock.getPetByIdMock({ result: { id: 1, name: 'Fido' } }));

When called without arguments, the factory infers the base URL from the OpenAPI spec's servers field.

Handler Options

Handler options can be provided. The object will be passed on to MSW helpers.

ts
const mockServer = setupServer(getPetByIdMock({ result: { id: 1, name: 'Fido' } }, { once: true }));

Known Limitations

  • Query parameters are not typed in the resolver. MSW doesn't provide typed query params natively — use new URL(request.url).searchParams instead.
  • The response type generic is omitted from HttpResponseResolver to avoid MSW's DefaultBodyType constraint issues with union and void response types.

Web Src Content Docs Docs Openapi Typescript Plugins Nest (`web/src/content/docs/docs/openapi/typescript/plugins/nest.mdx`)


title: NestJS v11 Plugin sidebar: label: Nest custom: version: v11

import { Tabs, TabItem } from '@astrojs/starlight/components';
import AuthorsList from '@/components/AuthorsList.astro';
import Examples from '@/components/Examples.astro';
import FeatureStatus from '@/components/FeatureStatus.astro';
import UserConfig from '@/components/UserConfig.astro';
import { yuriMikhin } from '@/data/people';

NestJS

About

Nest is a progressive Node.js framework for building efficient, reliable and scalable server-side applications.

The NestJS plugin for Hey API generates type-safe controller method signatures from your OpenAPI spec, fully compatible with all core features.

Collaborators

Features

  • NestJS v11 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • type-safe controller methods
  • minimal learning curve thanks to extending the underlying technology

Installation

In your configuration, add nestjs to your plugins and you'll be ready to generate NestJS artifacts. 🎉

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    'nestjs',
  ],
};

Output

The NestJS plugin will generate the following artifacts, depending on the input specification.

Controller Methods

Operations are grouped by their first tag into separate types.

```ts title="nestjs.gen.ts" export type PetsControllerMethods = { createPet: (body: CreatePetData['body']) => Promise; listPets: (query?: ListPetsData['query']) => Promise; showPetById: (path: ShowPetByIdData['path']) => Promise; };

export type StoreControllerMethods = {
getInventory: () => Promise;
};

text
</TabItem>
  <TabItem label="usage">
```ts title="pets.controller.ts"
import { Body, Controller, Get, Param, Post, Query } from '@nestjs/common';

import type { PetsControllerMethods } from '../client/nestjs.gen';
import type { CreatePetData, ListPetsData, ShowPetByIdData } from '../client/types.gen';

@Controller('pets')
export class PetsController implements Pick<
  PetsControllerMethods,
  'createPet' | 'listPets' | 'showPetById'
> {
  @Post()
  async createPet(@Body() body: CreatePetData['body']) {}

  @Get()
  async listPets(@Query() query?: ListPetsData['query']) {}

  @Get(':petId')
  async showPetById(@Param() path: ShowPetByIdData['path']) {}
}

Web Src Content Docs Docs Openapi Typescript Plugins Nock (`web/src/content/docs/docs/openapi/typescript/plugins/nock.mdx`)


title: Nock

import FeatureStatus from '@/components/FeatureStatus.astro';

Nock

About

Nock is an HTTP server mocking and expectations library for Node.js.


Web Src Content Docs Docs Openapi Typescript Plugins Pinia Colada (`web/src/content/docs/docs/openapi/typescript/plugins/pinia-colada.mdx`)


title: Pinia Colada v0 Plugin sidebar: label: Pinia Colada custom: version: v0

import { Tabs, TabItem } from '@astrojs/starlight/components';
import AuthorsList from '@/components/AuthorsList.astro';
import Examples from '@/components/Examples.astro';
import UserConfig from '@/components/UserConfig.astro';
import { joshHemphill, sebastiaanWouters, dmitriyBrolnickij } from '@/data/people';

Pinia Colada

About

Pinia Colada is the perfect companion to Pinia to handle async state management in your Vue applications.

The Pinia Colada plugin for Hey API generates functions and query keys from your OpenAPI spec, fully compatible with SDKs, transformers, and all core features.

Collaborators

<AuthorsList people={[dmitriyBrolnickij, joshHemphill, sebastiaanWouters]} />

Features

  • Pinia Colada v0 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • create query keys following the best practices
  • type-safe query options and mutation options
  • minimal learning curve thanks to extending the underlying technology

Installation

In your configuration, add @pinia/colada to your plugins and you'll be ready to generate Pinia Colada artifacts. 🎉

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    '@pinia/colada',
  ],
};

:::tip
When using this plugin in a Nuxt app, prefer the ofetch client for universal compatibility.

The nuxt client is tailored for working directly with Nuxt composables ($fetch / useFetch / useAsyncData) and is not intended as a universal HTTP client for libraries like @pinia/colada.
:::

Output

The Pinia Colada plugin will generate the following artifacts, depending on the input specification.

Queries

Queries are generated from query operations. The generated query functions follow the naming convention of SDK functions and by default append Query, e.g., getPetByIdQuery().

```ts title="colada.gen.ts" const query = useQuery(getPetByIdQuery, () => ({ path: { petId: 1, }, })); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@pinia/colada', queryOptions: true, }, ], }; ```

You can customize the naming and casing pattern for queryOptions functions using the .name and .case options.

Query Keys

Query keys contain normalized SDK function parameters and additional metadata.

```ts title="colada.gen.ts" const queryKey = [ { _id: 'getPetById', baseUrl: 'https://app.heyapi.dev', path: { petId: 1, }, }, ]; ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@pinia/colada', queryKeys: true, }, ], }; ```

Tags

You can include operation tags in your query keys by setting tags to true. This will make query keys larger but provides better cache invalidation capabilities.

```ts {8} title="colada.gen.ts" const key = [ { _id: 'getPetById', baseUrl: 'https://app.heyapi.dev', path: { petId: 1, }, tags: ['pets', 'one', 'get'], }, ]; ``` ```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@pinia/colada', queryKeys: { tags: true, }, }, ], }; ```

Accessing Query Keys

If you have access to the result of query options function, you can get the query key from the key field.

```ts title="colada.gen.ts" const { key } = getPetByIdQuery({ path: { petId: 1, }, }); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@pinia/colada', queryOptions: true, }, ], }; ```

Alternatively, you can access the same query key by calling query key functions. The generated query key functions follow the naming convention of SDK functions and by default append QueryKey, e.g., getPetByIdQueryKey().

```ts title="colada.gen.ts" const key = getPetByIdQueryKey({ path: { petId: 1, }, }); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@pinia/colada', queryKeys: true, }, ], }; ```

You can customize the naming and casing pattern for queryKeys functions using the .name and .case options.

Mutations

Mutations are generated from mutation operations. The generated mutation functions follow the naming convention of SDK functions and by default append Mutation, e.g., addPetMutation().

```ts title="colada.gen.ts" const addPet = useMutation({ ...addPetMutation(), onError: (error) => { console.log(error); }, });

addPet.mutate({
body: {
name: 'Kitty',
},
});

text
</TabItem>
  <TabItem label="config">
```js {8} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@pinia/colada',
      mutationOptions: true,
    },
  ],
};

You can customize the naming and casing pattern for mutationOptions functions using the .name and .case options.


Web Src Content Docs Docs Openapi Typescript Plugins Playwright (`web/src/content/docs/docs/openapi/typescript/plugins/playwright.mdx`)


title: Playwright

import FeatureStatus from '@/components/FeatureStatus.astro';

Playwright

About

Playwright is an end-to-end test framework for modern web apps. It bundles test runner, assertions, isolation, parallelization and rich tooling.


Web Src Content Docs Docs Openapi Typescript Plugins Schemas (`web/src/content/docs/docs/openapi/typescript/plugins/schemas.mdx`)


title: JSON Schema sidebar: label: Schemas

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import UserConfig from '@/components/UserConfig.astro';

JSON Schemas

Schemas are located in the schemas.gen.ts file. This file contains runtime schemas generated from your OpenAPI specification definitions located in #/components/schemas. If you're using OpenAPI 3.1, your schemas are fully JSON Schema compliant and can be used with other tools supporting JSON Schema.

Configuration

You can modify the contents of schemas.gen.ts by configuring the @hey-api/schemas plugin. Note that you must specify the default plugins to preserve the default output.

```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@hey-api/schemas', type: 'json', }, ], }; ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@hey-api/schemas', type: 'form', }, ], }; ``` ```js del={6} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins '@hey-api/schemas', ], }; ```

Output

Below is an example output generated in the type: 'form' style. Disabling schemas will not generate the schemas.gen.ts file.

ts title="schemas.gen.ts"
export const PetSchema = {
  required: ['name'],
  properties: {
    id: {
      type: 'integer',
      format: 'int64',
      example: 10,
    },
    name: {
      type: 'string',
      example: 'doggie',
    },
  },
  type: 'object',
} as const;

Usage

A great use case for schemas is client-side form input validation.

ts title="somecomponent.ts"
import { $Schema } from './client/schemas.gen';

const maxInputLength = $Schema.properties.text.maxLength;

if (userInput.length > maxInputLength) {
  throw new Error(`Text length can't exceed ${maxInputLength} characters!`);
}

Web Src Content Docs Docs Openapi Typescript Plugins Sdk (`web/src/content/docs/docs/openapi/typescript/plugins/sdk.mdx`)


title: SDK Plugin sidebar: label: SDK

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import UserConfig from '@/components/UserConfig.astro';

SDK

About

The SDK plugin generates a high-level, ergonomic API layer on top of the low-level HTTP client.

It exposes typed functions or methods for each operation, with built-in auth handling, configurable request and response validation, and ready-to-use code examples.

Features

  • high-level SDK layer on top of the HTTP client
  • typed functions or methods per operation
  • built-in authentication handling
  • request and response validation
  • ready-to-use code examples

Installation

In your configuration, add @hey-api/sdk to your plugins and you'll be ready to generate SDK artifacts. 🎉

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    '@hey-api/sdk',
  ],
};

Output

The SDK plugin supports a wide range of configuration options. This guide focuses on two main SDK formats: tree-shakeable functions and instantiable classes, but you can apply the same concepts to create more advanced configurations.

Flat

This is the default setting. Flat SDKs support tree-shaking, which can lead to a reduced bundle size. You select flat mode by setting operations.strategy to flat.

```ts title="sdk.gen.ts" import type { AddPetData } from './types.gen';

export const addPet = (options: Options) => {
/** ... */
};

text
</TabItem>
  <TabItem label="config">
```js {9} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@hey-api/sdk',
      operations: {
        strategy: 'flat',
      },
    },
  ],
};

Instance

Class SDKs do not support tree-shaking, which results in a larger bundle size, but you may prefer their syntax. You select class mode by setting operations.strategy to single.

```ts title="sdk.gen.ts" import type { AddPetData } from './types.gen';

export class Sdk extends HeyApiClient {
public addPet(options: Options) {
/** ... */
}
}

text
</TabItem>
  <TabItem label="config">
```js {9} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@hey-api/sdk',
      operations: {
        strategy: 'single',
      },
    },
  ],
};

Name

As shown above, by default our SDK class is called Sdk. The first thing you'll likely want to do is change this to your preferred name, which you can do using operation.containerName.

```ts title="sdk.gen.ts" "PetStore" import { client } from './client.gen'; import type { AddPetData, AddPetErrors, AddPetResponses } from './types.gen';

export class PetStore extends HeyApiClient {
/** ... */
}

text
</TabItem>
  <TabItem label="config">
```js {9} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@hey-api/sdk',
      operations: {
        containerName: 'PetStore',
        strategy: 'single',
      },
    },
  ],
};

Structure

While we try to infer the SDK structure from operationId fields, you'll likely want to customize it further. You can do this using operations.nesting.

Similar to the operations.strategy option, we provide a few presets. However, you gain the most control by providing your own function.

To demonstrate the power of this feature, let's nest a few endpoints inside a Pet class and rename them. Our original addPet() method will now become pet.add(). Notice that we use the built-in OperationPath.fromOperationId() helper to handle the remaining operations.

```ts {5,11} title="sdk.gen.ts" import { client } from './client.gen'; import type { AddPetData, AddPetErrors, AddPetResponses } from './types.gen';

export class Pet extends HeyApiClient {
public add(options: Options) {
/** ... */
}
}

export class PetStore extends HeyApiClient {
get pet(): Pet {
/** ... */
}
}

text
</TabItem>
  <TabItem label="config">
```js {10-18} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@hey-api/sdk',
      operations: {
        containerName: 'PetStore',
        nesting(operation) {
          if (operation.path === '/pet/{petId}' || operation.path === '/pet') {
            return [
              'pet',
              operation.operationId?.replace(/Pet/, '') || operation.method.toLocaleLowerCase(),
            ];
          }
          return OperationPath.fromOperationId()(operation);
        },
        strategy: 'single',
      },
    },
  ],
};

Parameters

By default, all SDK methods accept an object with namespaced properties for each parameter type: query, path, header, and body.

```ts title="app.ts" import { addPet } from './sdk.gen.ts';

await addPet({
body: {
name: 'Fluffy',
type: 'cat',
},
path: {
petId: 1234,
},
query: {
color: 'red',
},
});

text
</TabItem>
  <TabItem label="config">
```js {8} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@hey-api/sdk',
      paramsStructure: 'grouped',
    },
  ],
};

If you prefer a more compact syntax, you can set paramsStructure to flat. This will merge all parameter types into a single object, which is especially useful for endpoints with only one parameter type.

```ts title="app.ts" import { addPet } from './sdk.gen.ts';

await addPet({
color: 'red',
name: 'Fluffy',
petId: 1234,
type: 'cat',
});

text
</TabItem>
  <TabItem label="config">
```js {8} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@hey-api/sdk',
      paramsStructure: 'flat',
    },
  ],
};

Auth

Most APIs require some form of authentication, which is why the SDK plugin provides built-in auth mechanisms by default. All you need to do is return the data from the auth() function, and the SDK will handle serialization and encoding for you. There are several ways to do this, for example on the client instance.

```ts title="sdk.gen.ts" import { client } from './client.gen';

client.setConfig({
auth() {
return '';
},
});

text
</TabItem>
  <TabItem label="config">
```js {7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      auth: true,
      name: '@hey-api/sdk',
    },
  ],
};

:::note
The SDK plugin currently supports only the bearer and basic auth schemes. Open an issue if you'd like support for additional mechanisms.
:::

Validators

Validating data at runtime comes with a performance cost, which is why it's not enabled by default. To enable validation, set validator to zod or one of the available validator plugins. This will automatically add the selected plugin with its default configuration.

For a more granular approach, manually add a validator plugin and set validator to the plugin name or true to automatically select a compatible plugin. Until you customize the validator plugin, both approaches will produce the same default output.

```ts {5-8} title="sdk.gen.ts" import * as v from 'valibot';

export const addPet = (options: Options) =>
(options.client ?? client).post<AddPetResponses, AddPetErrors>({
requestValidator: async (data) =>
await v.parseAsync(vAddPetData, data),
responseValidator: async (data) =>
await v.parseAsync(vAddPetResponse, data),
/** ... */
});

text
</TabItem>
  <TabItem label="config">
```js {8,11} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@hey-api/sdk',
      validator: true, // or 'valibot'
    },
    {
      name: 'valibot', // customize (optional)
      // other options
    },
  ],
};

You can choose to validate only requests or responses.

```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@hey-api/sdk', validator: { request: 'zod', }, }, ], }; ``` ```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@hey-api/sdk', validator: { response: 'zod', }, }, ], }; ```

Learn more about available validators on the Validators page.

Transformers

Transformers work similarly to validators, with two differences: transformation applies to responses only, and you use the transformer option instead of validator.

```ts {5-6} title="sdk.gen.ts" import * as v from 'valibot';

export const addPet = (options: Options) =>
(options.client ?? client).post<AddPetResponses, AddPetErrors>({
responseTransformer: async (data) =>
await v.parseAsync(vAddPetResponse, data),
/** ... */
});

text
</TabItem>
  <TabItem label="config">
```js {8,11} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@hey-api/sdk',
      transformer: true, // or 'valibot'
    },
    {
      name: 'valibot', // customize (optional)
      // other options
    },
  ],
};

Learn more about available transformers on the Validators page.

Metadata

Every SDK method accepts an optional meta object. You can use it to pass arbitrary values that aren't part of the SDK function and read them back in your client interceptors or middleware.

ts title="app.ts"
import { addPet } from './sdk.gen.ts';

await addPet({
  meta: {
    timeout: 5000,
  },
});

By default meta is untyped, so typos and wrong-typed values compile silently. To make it type-safe, augment the ClientMeta interface exported by your client. After augmentation, meta is fully type-checked at the call site.

```ts title="overrides.d.ts" declare module '@hey-api/client-fetch' { interface ClientMeta { retry?: boolean; timeout?: number; } } ``` ```ts title="app.ts" import { addPet } from './sdk.gen.ts';

await addPet({
meta: {
timeout: 5000, // ✅ correctly typed
timeoutt: 5000, // ❌ unknown key
retry: '5s', // ❌ wrong type
},
});

text
</TabItem>
</Tabs>

## Code Examples

The SDK plugin can generate ready-to-use code examples for each operation, showing how to call the SDK methods with proper parameters and setup.

Examples are not generated by default, but you can enable and customize them through the `examples` option. With the default settings, an example might look like this.

<Tabs>
  <TabItem label="example">
```ts
import { PetStore } from 'your-package';

await new PetStore().addPet();
```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { examples: true, name: '@hey-api/sdk', operations: { containerName: 'PetStore', strategy: 'single', }, }, ], }; ```

Module and Setup

To make examples more practical, configure moduleName to specify the package from which users import your SDK.

Next, set setupName to indicate how users should instantiate the SDK, typically only once per application.

```ts "@petstore/client" "client" import { PetStore } from '@petstore/client';

const client = new PetStore();

await client.addPet();

text
</TabItem>
  <TabItem label="config">
```js {8-9} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      examples: {
        moduleName: '@petstore/client',
        setupName: 'client',
      },
      name: '@hey-api/sdk',
      operations: {
        containerName: 'PetStore',
        strategy: 'single',
      },
    },
  ],
};

Initialization

Often, your SDK needs to be instantiated with an API key or other configuration. In examples, importSetup lets you control how the SDK is initialized.

```ts {3-5} import { PetStore } from '@petstore/client';

const client = new PetStore({
apiKey: 'YOUR_API_KEY',
});

await client.addPet();

text
</TabItem>
  <TabItem label="config">
```js {8-14} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      examples: {
        importSetup: ({ $, node }) =>
          $.new(
            node.name,
            $.object()
              .pretty()
              .prop('apiKey', $.literal('YOUR_API_KEY')),
          ),
        moduleName: '@petstore/client',
        setupName: 'client',
      },
      name: '@hey-api/sdk',
      operations: {
        containerName: 'PetStore',
        strategy: 'single',
      },
    },
  ],
};

Import Style

If you re-export the generated SDK from your own module, you can adjust importName and importKind to match your actual import style.

```ts {1} "CatStore" import CatStore from '@petstore/client';

const client = new CatStore({
apiKey: 'YOUR_API_KEY',
});

await client.addPet();

text
</TabItem>
  <TabItem label="config">
```js {8-9} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      examples: {
        importKind: 'default',
        importName: 'CatStore',
        importSetup: ({ $, node }) =>
          $.new(
            node.name,
            $.object()
              .pretty()
              .prop('apiKey', $.literal('YOUR_API_KEY')),
          ),
        moduleName: '@petstore/client',
        setupName: 'client',
      },
      name: '@hey-api/sdk',
      operations: {
        containerName: 'PetStore',
        strategy: 'single',
      },
    },
  ],
};

Payload

You can customize the example request using the payload option. Requests can also be customized selectively. For example, we can provide a default payload only for the addPet() method.

```ts {7-9} import CatStore from '@petstore/client';

const client = new CatStore({
apiKey: 'YOUR_API_KEY',
});

await client.addPet({
petId: 1234,
});

text
</TabItem>
  <TabItem label="config">
```js {18-23} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      examples: {
        importKind: 'default',
        importName: 'CatStore',
        importSetup: ({ $, node }) =>
          $.new(
            node.name,
            $.object()
              .pretty()
              .prop('apiKey', $.literal('YOUR_API_KEY')),
          ),
        moduleName: '@petstore/client',
        payload(operation, ctx) {
          const { $ } = ctx;
          if (operation.path === '/pet/{petId}' || operation.path === '/pet') {
            return $.object().pretty().prop('petId', $.literal(1234));
          }
        },
        setupName: 'client',
      },
      name: '@hey-api/sdk',
      operations: {
        containerName: 'PetStore',
        strategy: 'single',
      },
    },
  ],
};

Display

Enabling examples does not produce visible output on its own. Examples are written into the source specification and can be consumed by documentation tools. To persist that specification, enable Source generation.


Web Src Content Docs Docs Openapi Typescript Plugins Superstruct (`web/src/content/docs/docs/openapi/typescript/plugins/superstruct.mdx`)


title: Superstruct

import FeatureStatus from '@/components/FeatureStatus.astro';

Superstruct

About

Superstruct makes it easy to define interfaces and then validate JavaScript data against them.


Web Src Content Docs Docs Openapi Typescript Plugins Supertest (`web/src/content/docs/docs/openapi/typescript/plugins/supertest.mdx`)


title: Supertest

import FeatureStatus from '@/components/FeatureStatus.astro';

Supertest

About

Supertest is a super-agent driven library for testing node.js HTTP servers using a fluent API.


Web Src Content Docs Docs Openapi Typescript Plugins Swr (`web/src/content/docs/docs/openapi/typescript/plugins/swr.mdx`)


title: SWR sidebar: label: SWR

import FeatureStatus from '@/components/FeatureStatus.astro';

SWR

About

SWR is a strategy to first return the data from cache (stale), then send the fetch request (revalidate), and finally come with the up-to-date data.


Web Src Content Docs Docs Openapi Typescript Plugins Tanstack Query (`web/src/content/docs/docs/openapi/typescript/plugins/tanstack-query.mdx`)


title: TanStack Query v5 Plugin sidebar: label: TanStack Query custom: version: v5

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import StackBlitz from '@/components/StackBlitz.astro';
import UserConfig from '@/components/UserConfig.astro';

TanStack Query

About

TanStack Query is a powerful asynchronous state management solution for TypeScript/JavaScript, React, Solid, Vue, Svelte, Angular, and Preact.

The TanStack Query plugin for Hey API generates functions and query keys from your OpenAPI spec, fully compatible with SDKs, transformers, and all core features.

Demo

<StackBlitz
example="hey-api-client-fetch-plugin-tanstack-react-query-example"
openFile={[
'openapi-ts.config.ts',
'src/client/@tanstack/react-query.gen.ts',
'src/client/types.gen.ts',
'src/App.tsx',
]}
/>

Features

  • TanStack Query v5 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • create query keys following the best practices
  • type-safe query options, infinite query options, and mutation options
  • minimal learning curve thanks to extending the underlying technology

Installation

In your configuration, add TanStack Query to your plugins and you'll be ready to generate TanStack Query artifacts. 🎉

```js {6} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins '@tanstack/react-query', ], }; ``` ```js {6} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins '@tanstack/vue-query', ], }; ``` ```js {6} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins '@tanstack/angular-query-experimental', ], }; ``` ```js {6} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins '@tanstack/svelte-query', ], }; ``` ```js {6} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins '@tanstack/solid-query', ], }; ``` ```js {6} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins '@tanstack/preact-query', ], }; ```

Output

The TanStack Query plugin will generate the following artifacts, depending on the input specification.

Queries

Queries are generated from query operations. The generated query functions follow the naming convention of SDK functions and by default append Options, e.g., getPetByIdOptions().

```ts title="react-query.gen.ts" const query = useQuery({ ...getPetByIdOptions({ path: { petId: 1, }, }), }); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@tanstack/react-query', queryOptions: true, }, ], }; ```

You can customize the naming and casing pattern for queryOptions functions using the .name and .case options.

Meta

You can use the meta field to attach arbitrary information to a query. To generate metadata for queryOptions, provide a function to the .meta option.

```ts title="react-query.gen.ts" queryOptions({ // ...other fields meta: { id: 'getPetById', }, }); ``` ```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@tanstack/react-query', queryOptions: { meta: (operation) => ({ id: operation.id }), }, }, ], }; ```

Query Keys

Query keys contain normalized SDK function parameters and additional metadata.

```ts title="react-query.gen.ts" const queryKey = [ { _id: 'getPetById', baseUrl: 'https://app.heyapi.dev', path: { petId: 1, }, }, ]; ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@tanstack/react-query', queryKeys: true, }, ], }; ```

Tags

You can include operation tags in your query keys by setting tags to true. This will make query keys larger but provides better cache invalidation capabilities.

```ts {8} title="react-query.gen.ts" const queryKey = [ { _id: 'getPetById', baseUrl: 'https://app.heyapi.dev', path: { petId: 1, }, tags: ['pets', 'one', 'get'], }, ]; ``` ```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@tanstack/react-query', queryKeys: { tags: true, }, }, ], }; ```

Accessing Query Keys

If you have access to the result of query options function, you can get the query key from the queryKey field.

```ts title="react-query.gen.ts" const { queryKey } = getPetByIdOptions({ path: { petId: 1, }, }); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@tanstack/react-query', queryOptions: true, }, ], }; ```

Alternatively, you can access the same query key by calling query key functions. The generated query key functions follow the naming convention of SDK functions and by default append QueryKey, e.g., getPetByIdQueryKey().

```ts title="react-query.gen.ts" const queryKey = getPetByIdQueryKey({ path: { petId: 1, }, }); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@tanstack/react-query', queryKeys: true, }, ], }; ```

You can customize the naming and casing pattern for queryKeys functions using the .name and .case options.

Infinite Queries

Infinite queries are generated from query operations if we detect a pagination parameter. The generated infinite query functions follow the naming convention of SDK functions and by default append InfiniteOptions, e.g., getFooInfiniteOptions().

```ts title="react-query.gen.ts" const query = useInfiniteQuery({ ...getFooInfiniteOptions({ path: { fooId: 1, }, }), getNextPageParam: (lastPage, pages) => lastPage.nextCursor, initialPageParam: 0, }); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@tanstack/react-query', infiniteQueryOptions: true, }, ], }; ```

You can customize the naming and casing pattern for infiniteQueryOptions functions using the .name and .case options.

Meta

You can use the meta field to attach arbitrary information to a query. To generate metadata for infiniteQueryOptions, provide a function to the .meta option.

```ts title="react-query.gen.ts" infiniteQueryOptions({ // ...other fields meta: { id: 'getPetById', }, }); ``` ```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@tanstack/react-query', infiniteQueryOptions: { meta: (operation) => ({ id: operation.id }), }, }, ], }; ```

Infinite Query Keys

Infinite query keys contain normalized SDK function parameters and additional metadata.

```ts title="react-query.gen.ts" const queryKey = [ { _id: 'getPetById', _infinite: true, baseUrl: 'https://app.heyapi.dev', path: { petId: 1, }, }, ]; ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@tanstack/react-query', infiniteQueryKeys: true, }, ], }; ```

Tags

You can include operation tags in your infinite query keys by setting tags to true. This will make query keys larger but provides better cache invalidation capabilities.

```ts {9} title="react-query.gen.ts" const queryKey = [ { _id: 'getPetById', _infinite: true, baseUrl: 'https://app.heyapi.dev', path: { petId: 1, }, tags: ['pets', 'one', 'get'], }, ]; ``` ```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@tanstack/react-query', infiniteQueryKeys: { tags: true, }, }, ], }; ```

Accessing Infinite Query Keys

If you have access to the result of infinite query options function, you can get the query key from the queryKey field.

```ts title="react-query.gen.ts" const { queryKey } = getPetByIdInfiniteOptions({ path: { petId: 1, }, }); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@tanstack/react-query', infiniteQueryOptions: true, }, ], }; ```

Alternatively, you can access the same query key by calling query key functions. The generated query key functions follow the naming convention of SDK functions and by default append InfiniteQueryKey, e.g., getPetByIdInfiniteQueryKey().

```ts title="react-query.gen.ts" const queryKey = getPetByIdInfiniteQueryKey({ path: { petId: 1, }, }); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@tanstack/react-query', infiniteQueryKeys: true, }, ], }; ```

You can customize the naming and casing pattern for infiniteQueryKeys functions using the .name and .case options.

Mutations

Mutations are generated from mutation operations. The generated mutation functions follow the naming convention of SDK functions and by default append Mutation, e.g., addPetMutation().

```ts title="react-query.gen.ts" const addPet = useMutation({ ...addPetMutation(), onError: (error) => { console.log(error); }, });

addPet.mutate({
body: {
name: 'Kitty',
},
});

text
</TabItem>
  <TabItem label="config">
```js {8} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: '@tanstack/react-query',
      mutationOptions: true,
    },
  ],
};

You can customize the naming and casing pattern for mutationOptions functions using the .name and .case options.

Mutation Keys

Mutation keys contain normalized SDK function parameters and additional metadata.

```ts title="react-query.gen.ts" const mutationKey = [ { _id: 'addPet', baseUrl: 'https://app.heyapi.dev', body: { name: 'Kitty', }, }, ]; ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@tanstack/react-query', mutationKeys: true, }, ], }; ```

Meta

You can use the meta field to attach arbitrary information to a mutation. To generate metadata for mutationOptions, provide a function to the .meta option.

```ts title="react-query.gen.ts" const mutationOptions = { // ...other fields meta: { id: 'getPetById', }, }; ``` ```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@tanstack/react-query', mutationOptions: { meta: (operation) => ({ id: operation.id }), }, }, ], }; ```

Reactivity

In Vue applications, you need to wrap the options functions in computed() to make them reactive. Otherwise, TanStack Query won't know it should execute the query when its dependencies change.

```js title="react-query.gen.ts" // ✅ Query will execute on `petId` change const query = useQuery( computed(() => getPetByIdOptions({ path: { petId: petId.value, }, }), ), ); ``` ```js title="react-query.gen.ts" // ❌ Query will execute only once const query = useQuery( getPetByIdOptions({ path: { petId: petId.value, }, }), ); ```

Web Src Content Docs Docs Openapi Typescript Plugins Tanstack Start (`web/src/content/docs/docs/openapi/typescript/plugins/tanstack-start.mdx`)


title: TanStack Start

import FeatureStatus from '@/components/FeatureStatus.astro';

TanStack Start

About

TanStack Start is a full-stack framework powered by TanStack Router for React and Solid.


Web Src Content Docs Docs Openapi Typescript Plugins Transformers (`web/src/content/docs/docs/openapi/typescript/plugins/transformers.mdx`)


title: Transformers

import { Tabs, TabItem } from '@astrojs/starlight/components';
import UserConfig from '@/components/UserConfig.astro';

Transformers

:::tip
If you're already using a validator plugin such as Zod or Valibot, you can use it as a transformer too. See your validator plugin's docs for details.

Otherwise, @hey-api/transformers is the right choice.
:::

JSON is the most commonly used data format in REST APIs. However, it does not map well to complex data types. For example, both regular strings and date strings become simple strings in JSON.

One approach to this problem is using a JSON superset. For most people, switching formats is not feasible. That's why we provide the @hey-api/transformers plugin.

Considerations

Before deciding whether transformers are right for you, let's explain how they work. Transformers generate a runtime file, therefore they impact the bundle size. We generate a single transformer per operation response for the most efficient result, just like a human engineer would.

Limitations

Transformers handle only the most common scenarios. Some of the known limitations are:

  • union types are not transformed (e.g., if you have multiple possible response shapes)
  • only types defined through $ref are transformed
  • error responses are not transformed

If your data isn't being transformed as expected, we encourage you to leave feedback on GitHub.

Installation

In your configuration, add @hey-api/transformers to your plugins and you'll be ready to generate transformers. 🎉

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    '@hey-api/transformers',
  ],
};

SDKs

To transform response data in your SDKs, set transformer to true on the SDK plugin.

js {7-10} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    '@hey-api/transformers',
    {
      name: '@hey-api/sdk',
      transformer: true,
    },
  ],
};

Dates

To convert date strings into Date objects, use the dates configuration option.

js {7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      dates: true,
      name: '@hey-api/transformers',
    },
  ],
};

This will generate types that use Date instead of string and appropriate transformers.

Temporal

To use the Temporal API via temporal-polyfill, set dates to temporal.

```ts title="transformers.gen.ts" import { Temporal } from 'temporal-polyfill';

import type { GetFooResponse } from './types.gen';

const quxSchemaResponseTransformer = (data: any) => {
if (data.baz) {
data.baz = Temporal.Instant.from(data.baz);
}
return data;
};

const bazSchemaResponseTransformer = (data: any) => {
data = quxSchemaResponseTransformer(data);
data.bar = Temporal.Instant.from(data.bar);
return data;
};

export const getFooResponseTransformer = async (data: any): Promise => {
data = bazSchemaResponseTransformer(data);
return data;
};

text
</TabItem>
  <TabItem label="config">
```js {7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      dates: 'temporal',
      name: '@hey-api/transformers',
    },
  ],
};

BigInt

The @hey-api/transformers plugin will natively type all BigInts as bigint instead of number, which can affect arithmetic operations if your application previously used number. To force BigInts to be numbers, use the bigInt configuration option.

js {7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      bigInt: true,
      name: '@hey-api/transformers',
    },
  ],
};

Example

A generated response transformer might look something like this. Please note the example has been edited for brevity.

```ts title="transformers.gen.ts" import type { GetFooResponse } from './types.gen';

const quxSchemaResponseTransformer = (data: any) => {
if (data.baz) {
data.baz = new Date(data.baz);
}
return data;
};

const bazSchemaResponseTransformer = (data: any) => {
data = quxSchemaResponseTransformer(data);
data.bar = new Date(data.bar);
return data;
};

export const getFooResponseTransformer = async (data: any): Promise => {
data = bazSchemaResponseTransformer(data);
return data;
};

text
</TabItem>
  <TabItem label="types.gen.ts">
```ts title="types.gen.ts"
export type Baz = Qux & {
  id: 'Baz';
} & {
  foo: number;
  bar: Date;
  baz: 'foo' | 'bar' | 'baz';
  qux: number;
};

export type Qux = {
  foo: number;
  bar: number;
  baz?: Date;
  id: string;
};

export type GetFooResponse = Baz;

Web Src Content Docs Docs Openapi Typescript Plugins Typebox (`web/src/content/docs/docs/openapi/typescript/plugins/typebox.mdx`)


title: TypeBox

import FeatureStatus from '@/components/FeatureStatus.astro';

TypeBox

About

TypeBox is a JSON Schema type builder with static type resolution for TypeScript.


Web Src Content Docs Docs Openapi Typescript Plugins Typescript (`web/src/content/docs/docs/openapi/typescript/plugins/typescript.mdx`)


title: TypeScript sidebar: label: TypeScript

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import UserConfig from '@/components/UserConfig.astro';

TypeScript

TypeScript interfaces are located in the types.gen.ts file. This is the only file that does not impact your bundle size and runtime performance. It will get discarded during build time, unless you configured to emit runtime enums.

Installation

In your configuration, add @hey-api/typescript to your plugins and you'll be ready to generate TypeScript artifacts. 🎉

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    '@hey-api/typescript',
  ],
};

:::note
The @hey-api/typescript plugin might be automatically added to your plugins if another plugin depends on it.
:::

Output

The TypeScript plugin will generate the following artifacts, depending on the input specification.

Requests

A single request type is generated for each endpoint. It may contain a request body, parameters, and headers.

ts title="types.gen.ts"
export type AddPetData = {
  body: {
    id?: number;
    name: string;
  };
  path?: never;
  query?: never;
  url: '/pets';
};

You can customize the naming and casing pattern for requests types using the .name and .case options.

Responses

A single type is generated for all endpoint's responses.

ts title="types.gen.ts"
export type AddPetResponses = {
  200: {
    id?: number;
    name: string;
  };
};

export type AddPetResponse = AddPetResponses[keyof AddPetResponses];

You can customize the naming and casing pattern for responses types using the .name and .case options.

Definitions

A type is generated for every reusable definition from your input.

ts title="types.gen.ts"
export type Pet = {
  id?: number;
  name: string;
};

You can customize the naming and casing pattern for definitions types using the .name and .case options.

Enums

By default, @hey-api/typescript will emit enums only as types. You may want to generate runtime artifacts. A good use case is iterating through possible field values without manually typing arrays. To emit runtime enums, set enums to a valid option.

```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { enums: false, // default name: '@hey-api/typescript', }, ], }; ``` ```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { enums: 'javascript', name: '@hey-api/typescript', }, ], }; ``` ```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { enums: 'typescript', name: '@hey-api/typescript', }, ], }; ```

We recommend exporting enums as plain JavaScript objects. TypeScript enums are not a type-level extension of JavaScript and pose typing challenges.

Comments

By default, @hey-api/typescript will include comments in the generated code based on descriptions from your OpenAPI specification. If you want to reduce the size of your generated files or prefer cleaner output, you can disable comments.

```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { comments: true, // default name: '@hey-api/typescript', }, ], }; ``` ```js {7} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { comments: false, name: '@hey-api/typescript', }, ], }; ```

Resolvers

You can further customize this plugin's behavior using resolvers.


Web Src Content Docs Docs Openapi Typescript Plugins Valibot (`web/src/content/docs/docs/openapi/typescript/plugins/valibot.mdx`)


title: Valibot v1 Plugin sidebar: label: Valibot custom: version: v1

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import UserConfig from '@/components/UserConfig.astro';

Valibot

About

Valibot is the open source schema library for TypeScript with bundle size, type safety and developer experience in mind.

The Valibot plugin for Hey API generates schemas from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

Features

  • Valibot v1 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • Valibot schemas for requests, responses, and reusable definitions
  • minimal learning curve thanks to extending the underlying technology

Installation

In your configuration, add valibot to your plugins and you'll be ready to generate Valibot artifacts. 🎉

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    'valibot',
  ],
};

SDKs

Valibot can validate or transform data directly in your SDK. Set validator or transformer to true on the SDK plugin to enable it.

```js {7-10} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins 'valibot', { name: '@hey-api/sdk', validator: true, }, ], }; ``` ```js {7-10} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins 'valibot', { name: '@hey-api/sdk', transformer: true, }, ], }; ```

The SDK page covers validators and transformers in more detail.

Output

The Valibot plugin will generate the following artifacts, depending on the input specification.

Requests

A Valibot schema is generated for every request layer of each endpoint.

```ts title="valibot.gen.ts" const vDeletePetHeaders = v.object({ api_key: v.optional(v.string()), });

const vDeletePetPath = v.object({
petId: v.number(),
});

const vDeletePetQuery = v.object({
additionalMetadata: v.string(),
});

text
</TabItem>
  <TabItem label="config">
```js {8} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'valibot',
      requests: true,
    },
  ],
};

You can customize the naming and casing pattern for requests schemas using the .name and .case options.

Responses

A single Valibot schema is generated for all endpoint's responses. If the endpoint describes multiple responses, the generated schema is a union of all possible response shapes.

```ts title="valibot.gen.ts" const vResponse = v.union([ v.object({ foo: v.optional(v.string()), }), v.object({ bar: v.optional(v.number()), }), ]); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'valibot', responses: true, }, ], }; ```

You can customize the naming and casing pattern for responses schemas using the .name and .case options.

Definitions

A Valibot schema is generated for every reusable definition from your input.

```ts title="valibot.gen.ts" const vFoo = v.pipe(v.number(), v.integer());

const vBar = v.object({
bar: v.optional(v.array(v.pipe(v.number(), v.integer()))),
});

text
</TabItem>
  <TabItem label="config">
```js {8} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'valibot',
      definitions: true,
    },
  ],
};

You can customize the naming and casing pattern for definitions schemas using the .name and .case options.

Metadata

It's often useful to associate a schema with some additional metadata for documentation, code generation, AI structured outputs, form validation, and other purposes. You can set metadata to true to attach descriptions to schemas when available.

```ts title="valibot.gen.ts" export const vFoo = v.pipe( v.string(), v.metadata({ description: 'Additional metadata', }), ); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'valibot', metadata: true, }, ], }; ```

For more control over metadata, you can provide your own function. It receives the source schema, the target node object, and the $ builder for populating metadata.

```ts title="valibot.gen.ts" export const vFoo = v.pipe( v.string(), v.metadata({ hasTitle: false, createdAt: 1735732800000, }), ); ``` ```js {8-11} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'valibot', metadata({ $, node, schema }) { node.prop('hasTitle', $.literal(Boolean(schema.title))); node.prop('createdAt', $.literal(Date.now())); }, }, ], }; ```

Resolvers

You can further customize this plugin's behavior using resolvers.


Web Src Content Docs Docs Openapi Typescript Plugins Yup (`web/src/content/docs/docs/openapi/typescript/plugins/yup.mdx`)


title: Yup

import FeatureStatus from '@/components/FeatureStatus.astro';

Yup

About

Yup is a schema builder for runtime value parsing and validation.


Web Src Content Docs Docs Openapi Typescript Plugins Zustand (`web/src/content/docs/docs/openapi/typescript/plugins/zustand.mdx`)


title: Zustand sidebar: label: Zustand

import FeatureStatus from '@/components/FeatureStatus.astro';

Zustand

About

Zustand is a small, fast, and scalable bearbones state management solution.


Web Src Content Docs Docs Openapi Typescript Clients Angular V19 (`web/src/content/docs/docs/openapi/typescript/clients/angular/v19.mdx`)


title: Angular v19 Client custom: version: v19

import { Tabs, TabItem } from '@astrojs/starlight/components';
import AuthorsList from '@/components/AuthorsList.astro';
import Examples from '@/components/Examples.astro';
import FeatureStatus from '@/components/FeatureStatus.astro';
import UserConfig from '@/components/UserConfig.astro';
import { maxScopp } from '@/data/people';

Angular

About

Angular is a web framework that empowers developers to build fast, reliable applications.

The Angular client for Hey API generates a type-safe client from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

Collaborators

Features

  • Angular v19 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • type-safe response data and errors
  • support for @Injectable() decorators
  • response data validation and transformation
  • access to the original request and response
  • granular request and response customization options
  • minimal learning curve thanks to extending the underlying technology
  • support bundling inside the generated output

Installation

In your configuration, add @hey-api/client-angular to your plugins and you'll be ready to generate client artifacts. 🎉

```js {4} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: ['@hey-api/client-angular'], }; ``` ```sh {4} npx @hey-api/openapi-ts \ -i hey-api/backend \ -o src/client \ -c @hey-api/client-angular ```

Providers

You can use the Angular client in your application by adding provideHeyApiClient to your providers.

ts {6} title="src/app/app.module.ts"
import { provideHeyApiClient, client } from './client/client.gen';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(withFetch()),
    provideHeyApiClient(client),
  ],
};

Configuration

The Angular client is built as a thin wrapper on top of Angular, extending its functionality to work with Hey API. If you're already familiar with Angular, configuring your client will feel like working directly with Angular.

When we installed the client above, it created a client.gen.ts file. You will most likely want to configure the exported client instance. There are two ways to do that.

`setConfig()`

This is the simpler approach. You can call the setConfig() method at the beginning of your application or anytime you need to update the client configuration. You can pass any HttpRequest configuration option to setConfig(), and even your own httpClient implementation.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  baseUrl: 'https://example.com',
});

The disadvantage of this approach is that your code may call the client instance before it's configured for the first time. Depending on your use case, you might need to use the second approach.

Runtime API

Since client.gen.ts is a generated file, we can't directly modify it. Instead, we can tell our configuration to use a custom file implementing the Runtime API. We do that by specifying the runtimeConfigPath option.

js {7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    {
      name: '@hey-api/client-angular',
      runtimeConfigPath: './src/hey-api.ts',
    },
  ],
};

In our custom file, we need to export a createClientConfig() method. This function is a simple wrapper allowing us to override configuration values.

ts title="src/hey-api.ts"
import type { CreateClientConfig } from './client/client.gen';

export const createClientConfig: CreateClientConfig = (config) => ({
  ...config,
  baseUrl: 'https://example.com',
});

With this approach, client.gen.ts will call createClientConfig() before initializing the client instance. If needed, you can still use setConfig() to update the client configuration later.

`createClient()`

You can also create your own client instance. You can use it to manually send requests or point it to a different domain.

js title="src/index.ts"
import { createClient } from './client/client';

const myClient = createClient({
  baseUrl: 'https://example.com',
});

You can also pass this instance to any SDK function through the client option. This will override the default instance from client.gen.ts.

js title="src/index.ts"
const response = await getFoo({
  client: myClient,
});

SDKs

Alternatively, you can pass the client configuration options to each SDK function. This is useful if you don't want to create a client instance for one-off use cases.

js title="src/index.ts"
const response = await getFoo({
  baseUrl: 'https://example.com', // <-- override default configuration
});

`@Injectable`

If you prefer to use the @Injectable() decorators, set the asClass option in your SDK plugin to true.

```ts title="src/foo.service.ts" @Injectable({ providedIn: 'root' }) export class FooService { // class methods } ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ '@hey-api/client-angular', { name: '@hey-api/sdk', asClass: true, }, ], }; ```

Interceptors

:::caution
This section is under construction. We appreciate your patience.
:::

Auth

:::caution
This section is under construction. We appreciate your patience.
:::

Build URL

If you need to access the compiled URL, you can use the buildUrl() method. It's loosely typed by default to accept almost any value; in practice, you will want to pass a type hint.

ts title="src/index.ts"
type FooData = {
  path: {
    fooId: number;
  };
  query?: {
    bar?: string;
  };
  url: '/foo/{fooId}';
};

const url = client.buildUrl<FooData>({
  path: {
    fooId: 1,
  },
  query: {
    bar: 'baz',
  },
  url: '/foo/{fooId}',
});
console.log(url); // prints '/foo/1?bar=baz'

Custom Instance

You can provide a custom httpClient instance. This is useful if you need to extend the default instance with extra functionality, or replace it altogether.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  httpClient: inject(CustomHttpClient),
});

You can use any of the approaches mentioned in Configuration, depending on how granular you want your custom instance to be.

Plugins

You might be also interested in the Angular plugin.


Web Src Content Docs Docs Openapi Typescript Clients Angular V20 (`web/src/content/docs/docs/openapi/typescript/clients/angular/v20.mdx`)


title: Angular v20 Client sidebar: label: Angular custom: version: v20

import { Tabs, TabItem } from '@astrojs/starlight/components';
import AuthorsList from '@/components/AuthorsList.astro';
import Examples from '@/components/Examples.astro';
import FeatureStatus from '@/components/FeatureStatus.astro';
import UserConfig from '@/components/UserConfig.astro';
import { maxScopp } from '@/data/people';

Angular

About

Angular is a web framework that empowers developers to build fast, reliable applications.

The Angular client for Hey API generates a type-safe client from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

Collaborators

Features

  • Angular v20 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • type-safe response data and errors
  • support for @Injectable() decorators
  • response data validation and transformation
  • access to the original request and response
  • granular request and response customization options
  • minimal learning curve thanks to extending the underlying technology
  • support bundling inside the generated output

Installation

In your configuration, add @hey-api/client-angular to your plugins and you'll be ready to generate client artifacts. 🎉

```js {4} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: ['@hey-api/client-angular'], }; ``` ```sh {4} npx @hey-api/openapi-ts \ -i hey-api/backend \ -o src/client \ -c @hey-api/client-angular ```

Providers

You can use the Angular client in your application by adding provideHeyApiClient to your providers.

ts {6} title="src/app/app.module.ts"
import { provideHeyApiClient, client } from './client/client.gen';

export const appConfig: ApplicationConfig = {
  providers: [
    provideHttpClient(withFetch()),
    provideHeyApiClient(client),
  ],
};

Configuration

The Angular client is built as a thin wrapper on top of Angular, extending its functionality to work with Hey API. If you're already familiar with Angular, configuring your client will feel like working directly with Angular.

When we installed the client above, it created a client.gen.ts file. You will most likely want to configure the exported client instance. There are two ways to do that.

`setConfig()`

This is the simpler approach. You can call the setConfig() method at the beginning of your application or anytime you need to update the client configuration. You can pass any HttpRequest configuration option to setConfig(), and even your own httpClient implementation.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  baseUrl: 'https://example.com',
});

The disadvantage of this approach is that your code may call the client instance before it's configured for the first time. Depending on your use case, you might need to use the second approach.

Runtime API

Since client.gen.ts is a generated file, we can't directly modify it. Instead, we can tell our configuration to use a custom file implementing the Runtime API. We do that by specifying the runtimeConfigPath option.

js {7} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    {
      name: '@hey-api/client-angular',
      runtimeConfigPath: './src/hey-api.ts',
    },
  ],
};

In our custom file, we need to export a createClientConfig() method. This function is a simple wrapper allowing us to override configuration values.

ts title="src/hey-api.ts"
import type { CreateClientConfig } from './client/client.gen';

export const createClientConfig: CreateClientConfig = (config) => ({
  ...config,
  baseUrl: 'https://example.com',
});

With this approach, client.gen.ts will call createClientConfig() before initializing the client instance. If needed, you can still use setConfig() to update the client configuration later.

`createClient()`

You can also create your own client instance. You can use it to manually send requests or point it to a different domain.

js title="src/index.ts"
import { createClient } from './client/client';

const myClient = createClient({
  baseUrl: 'https://example.com',
});

You can also pass this instance to any SDK function through the client option. This will override the default instance from client.gen.ts.

js title="src/index.ts"
const response = await getFoo({
  client: myClient,
});

SDKs

Alternatively, you can pass the client configuration options to each SDK function. This is useful if you don't want to create a client instance for one-off use cases.

js title="src/index.ts"
const response = await getFoo({
  baseUrl: 'https://example.com', // <-- override default configuration
});

`@Injectable`

If you prefer to use the @Injectable() decorators, set the asClass option in your SDK plugin to true.

```ts title="src/foo.service.ts" @Injectable({ providedIn: 'root' }) export class FooService { // class methods } ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ '@hey-api/client-angular', { name: '@hey-api/sdk', asClass: true, }, ], }; ```

Interceptors

:::caution
This section is under construction. We appreciate your patience.
:::

Auth

:::caution
This section is under construction. We appreciate your patience.
:::

Build URL

If you need to access the compiled URL, you can use the buildUrl() method. It's loosely typed by default to accept almost any value; in practice, you will want to pass a type hint.

ts title="src/index.ts"
type FooData = {
  path: {
    fooId: number;
  };
  query?: {
    bar?: string;
  };
  url: '/foo/{fooId}';
};

const url = client.buildUrl<FooData>({
  path: {
    fooId: 1,
  },
  query: {
    bar: 'baz',
  },
  url: '/foo/{fooId}',
});
console.log(url); // prints '/foo/1?bar=baz'

Custom Instance

You can provide a custom httpClient instance. This is useful if you need to extend the default instance with extra functionality, or replace it altogether.

js title="src/index.ts"
import { client } from 'client/client.gen';

client.setConfig({
  httpClient: inject(CustomHttpClient),
});

You can use any of the approaches mentioned in Configuration, depending on how granular you want your custom instance to be.

Plugins

You might be also interested in the Angular plugin.


Web Src Content Docs Docs Openapi Typescript Community Contributing Building (`web/src/content/docs/docs/openapi/typescript/community/contributing/building.mdx`)


title: Building

Building

:::caution
This page is under construction. We appreciate your patience.
:::

Prerequisites

You should have a working knowledge of git, node, and pnpm.

Guidelines

Your pull request must:

  • address a single issue or add a single item of functionality
  • contain a clean history of small, incremental, logically separate commits, with no merge commits
  • use clear commit messages
  • be possible to merge automatically

Start `@hey-api/openapi-ts`

Run pnpm --filter @hey-api/openapi-ts dev.


Web Src Content Docs Docs Openapi Typescript Community Contributing Developing (`web/src/content/docs/docs/openapi/typescript/community/contributing/developing.mdx`)


title: Developing

import { Tabs, TabItem } from '@astrojs/starlight/components';

Developing

:::caution
This page is under construction. We appreciate your patience.
:::

Working with Examples

The examples folder contains various integration examples that demonstrate how to use @hey-api/openapi-ts with different frameworks and libraries. These examples are kept in sync with the codebase through automated checks.

Generating Example Code

When you make changes to the core packages that affect code generation, you need to regenerate the client code in all examples:

sh
pnpm examples:update

This command will:

  • Find all examples with an openapi-ts script
  • Run the OpenAPI code generator for each example
  • Update the generated client code in each example

Checking Example Code

Before committing changes, ensure that all generated example code is up-to-date:

sh
pnpm examples:check

This command will:

  • Regenerate all example code
  • Check if any files were modified
  • Exit with an error if generated code is out of sync

This check is also run automatically in CI to ensure examples stay in sync with the main codebase.

Example Workflow

  1. Make changes to core packages
  2. Build the packages: pnpm build --filter="@hey-api/**"
  3. Regenerate examples: pnpm examples:update
  4. Commit all changes including the updated generated code
  5. The CI will verify that examples are in sync

:::tip
Think of generated example code as snapshot tests - they should always reflect the current state of the code generator.
:::

Writing Changelogs

We use Changesets to manage releases and generate changelogs. When contributing changes, create a changeset to document your updates.

Creating a Changeset

Run the following command to create a new changeset:

sh
pnpm changeset

This will prompt you to:

  1. Select the packages that were changed
  2. Choose the semver bump type (major, minor, or patch)
  3. Write a summary of your changes

Changeset Format

Changesets use the following format:

md title="example.changeset.md"
---
"@hey-api/openapi-ts": patch
---

**scope**: description of changes

Scopes:

  • cli, parser, output, config, input, internal, build, error → Core section
  • plugin(name) → Plugins section (e.g., **plugin(zod)**:, **plugin(@hey-api/client-axios)**:)
  • Any other scope → Other section

Breaking Changes:

  • Use **BREAKING**: prefix in the description to mark breaking changes
  • For packages on v0.x (major version 0), minor bumps may include breaking changes. Use signal words like "removed", "renamed", or "changed signature" to indicate breaking changes.

Examples

```md title="example.changeset.md" --- "@hey-api/openapi-ts": patch ---

parser: fix explicit discriminator mapping

text
</TabItem>
  <TabItem label="New feature">
```md title="example.changeset.md"
---
"@hey-api/openapi-ts": minor
---

**plugin(zod)**: handle guid string format
```md title="example.changeset.md" --- "@hey-api/openapi-ts": minor ---

BREAKING: removed deprecated getClient() function

text
</TabItem>
  <TabItem label="Plugin change">
```md title="example.changeset.md"
---
"@hey-api/openapi-ts": patch
---

**plugin(@hey-api/client-fetch)**: improve error handling

Web Src Content Docs Docs Openapi Typescript Community Contributing Testing (`web/src/content/docs/docs/openapi/typescript/community/contributing/testing.mdx`)


title: Testing

Testing

:::caution
This page is under construction. We appreciate your patience.
:::


Web Src Content Docs Docs Openapi Typescript Plugins Angular V19 (`web/src/content/docs/docs/openapi/typescript/plugins/angular/v19.mdx`)


title: Angular v19 Plugin custom: version: v19

import { Tabs, TabItem } from '@astrojs/starlight/components';
import AuthorsList from '@/components/AuthorsList.astro';
import Examples from '@/components/Examples.astro';
import FeatureStatus from '@/components/FeatureStatus.astro';
import UserConfig from '@/components/UserConfig.astro';
import { maxScopp } from '@/data/people';

Angular

About

Angular is a web framework that empowers developers to build fast, reliable applications.

The Angular plugin for Hey API generates HTTP requests and resources from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

Collaborators

Features

  • Angular v19 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • generate HTTP requests
  • generate HTTP resources
  • minimal learning curve thanks to extending the underlying technology

Installation

In your configuration, add @angular/common to your plugins and you'll be ready to generate Angular artifacts. 🎉

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    '@angular/common',
  ],
};

Output

The Angular plugin will generate the following artifacts, depending on the input specification.

Requests

A single function is generated for each endpoint. It returns an HttpRequest result.

```ts title="common.gen.ts" export const addPetRequest = (options) => client.requestOptions({ method: 'POST', responseStyle: 'data', url: '/pet', ...options, }); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@angular/common', httpRequests: true, }, ], }; ```

Resources

A single function is generated for each endpoint. It returns a result from httpResource call.

```ts title="common.gen.ts" export const addPetResource = (options) => httpResource(() => addPetRequest(options())); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@angular/common', httpResources: true, }, ], }; ```

Web Src Content Docs Docs Openapi Typescript Plugins Angular V20 (`web/src/content/docs/docs/openapi/typescript/plugins/angular/v20.mdx`)


title: Angular v20 Plugin sidebar: label: Angular custom: version: v20

import { Tabs, TabItem } from '@astrojs/starlight/components';
import AuthorsList from '@/components/AuthorsList.astro';
import Examples from '@/components/Examples.astro';
import FeatureStatus from '@/components/FeatureStatus.astro';
import UserConfig from '@/components/UserConfig.astro';
import { maxScopp } from '@/data/people';

Angular

About

Angular is a web framework that empowers developers to build fast, reliable applications.

The Angular plugin for Hey API generates HTTP requests and resources from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

Collaborators

Features

  • Angular v20 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • generate HTTP requests
  • generate HTTP resources
  • minimal learning curve thanks to extending the underlying technology

Installation

In your configuration, add @angular/common to your plugins and you'll be ready to generate Angular artifacts. 🎉

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    '@angular/common',
  ],
};

Output

The Angular plugin will generate the following artifacts, depending on the input specification.

Requests

A single function is generated for each endpoint. It returns an HttpRequest result.

```ts title="common.gen.ts" export const addPetRequest = (options) => client.requestOptions({ method: 'POST', responseStyle: 'data', url: '/pet', ...options, }); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@angular/common', httpRequests: true, }, ], }; ```

Resources

A single function is generated for each endpoint. It returns a result from httpResource call.

```ts title="common.gen.ts" export const addPetResource = (options) => httpResource(() => addPetRequest(options())); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: '@angular/common', httpResources: true, }, ], }; ```

Web Src Content Docs Docs Openapi Typescript Plugins Concepts Resolvers (`web/src/content/docs/docs/openapi/typescript/plugins/concepts/resolvers.mdx`)


title: Resolvers

import { Tabs, TabItem } from '@astrojs/starlight/components';

Resolvers

Sometimes the default plugin behavior isn't what you need or expect. Resolvers let you patch plugins in a safe and performant way, without forking or reimplementing core logic.

Currently available for TypeScript, Valibot, and Zod.

Examples

This page demonstrates resolvers through a few common scenarios.

  1. Handle arbitrary schema formats
  2. Validate high precision numbers
  3. Replace default base
  4. Create permissive enums

Terminology

Before we look at examples, let's go through the resolvers API to help you understand how they work. Plugins that support resolvers expose them through the $resolvers option. Each resolver is a function that receives context and returns an implemented node (or patches existing ones).

The resolver context will usually contain:

  • $ - The node builder interface. Use it to build your custom logic.
  • nodes - Parts of the plugin logic. You can use these to avoid reimplementing the functionality, or replace them with custom implementation.
  • plugin - The plugin instance. You'll most likely use it to register new symbols.
  • symbols - Frequently used symbols. These are effectively shorthands for commonly used plugin.referenceSymbol() calls.

Other fields may include the current schema or relevant utilities.

Example 1

Handle arbitrary schema formats

By default, the Valibot plugin may produce the following schemas for date and date-time strings.

js title="valibot.gen.ts"
export const vDates = v.object({
  created: v.pipe(v.string(), v.isoDate()),
  modified: v.pipe(v.string(), v.isoTimestamp()),
});

We can override this behavior by patching the nodes.format function only for strings with date or date-time formats.

js title="openapi-ts.config.ts"
{
  name: 'valibot',
  $resolvers: {
    string(ctx) {
      const { $, schema, symbols } = ctx;
      const { v } = symbols;
      if (schema.format === 'date' || schema.format === 'date-time') {
        ctx.nodes.format = () => $(v).attr('isoDateTime').call();
      }
    }
  }
}

This applies custom logic with surgical precision, without affecting the rest of the default behavior.

```js title="valibot.gen.ts" export const vDates = v.object({ created: v.pipe(v.string(), v.isoDateTime()), modified: v.pipe(v.string(), v.isoDateTime()), }); ``` ```js title="valibot.gen.ts" export const vDates = v.object({ created: v.pipe(v.string(), v.isoDate()), modified: v.pipe(v.string(), v.isoTimestamp()), }); ```

Example 2

Validate high precision numbers

Let's say you're dealing with very large or unsafe numbers.

js title="valibot.gen.ts"
export const vAmount = v.number();

In this case, you'll want to use a third-party library to validate your values. We can use big.js to validate all numbers by replacing the whole resolver.

js title="openapi-ts.config.ts"
{
  name: 'valibot',
  $resolvers: {
    number(ctx) {
      const { $, plugin, symbols } = ctx;
      const { v } = symbols;
      const big = plugin.symbolOnce('Big', {
        external: 'big.js',
        importKind: 'default',
      });
      return $(v).attr('instance').call(big);
    }
  }
}

We're calling plugin.symbolOnce() to ensure we always use the same symbol reference.

```js title="valibot.gen.ts" import Big from 'big.js';

export const vAmount = v.instance(Big);

text
</TabItem>
  <TabItem label="before">
```js title="valibot.gen.ts"
export const vAmount = v.number();

Example 3

Replace default base

You might want to replace the default base schema, e.g., v.object().

js title="valibot.gen.ts"
export const vUser = v.object({
  age: v.number(),
});

Let's say we want to interpret any schema without explicitly defined additional properties as a loose object.

js title="openapi-ts.config.ts"
{
  name: 'valibot',
  $resolvers: {
    object(ctx) {
      const { $, symbols } = ctx;
      const { v } = symbols;
      const additional = ctx.nodes.additionalProperties(ctx);
      if (additional === undefined) {
        const shape = ctx.nodes.shape(ctx);
        ctx.nodes.base = () => $(v).attr('looseObject').call(shape);
      }
    }
  }
}

Above we demonstrate patching a node based on the result of another node.

```js title="valibot.gen.ts" export const vUser = v.looseObject({ age: v.number(), }); ``` ```js title="valibot.gen.ts" export const vUser = v.object({ age: v.number(), }); ```

Example 4

Create permissive enums

By default, enum schemas are strict and will reject unknown values.

js title="zod.gen.ts"
export const zStatus = z.enum(['active', 'inactive', 'pending']);

You might want to accept unknown enum values, for example when the API adds new values that haven't been added to the spec yet. You can use the enum resolver to create a permissive union.

js title="openapi-ts.config.ts"
{
  name: 'zod',
  $resolvers: {
    enum(ctx) {
      const { $, symbols } = ctx;
      const { z } = symbols;
      const { enumMembers, literalSchemas } = ctx.nodes.items(ctx);

      if (!enumMembers.length || enumMembers.length !== literalSchemas.length) {
        return;
      }

      const enumSchema = $(z).attr('enum').call($.array(...enumMembers));
      return $(z).attr('union').call(
        $.array(enumSchema, $(z).attr('string').call())
      );
    }
  }
}

This resolver creates a union that accepts both the known enum values and any other string.

```js title="zod.gen.ts" export const zStatus = z.union([z.enum(['active', 'inactive', 'pending']), z.string()]); ``` ```js title="zod.gen.ts" export const zStatus = z.enum(['active', 'inactive', 'pending']); ```

Feedback

We welcome feedback on the Resolvers API. Open a GitHub issue to request support for additional plugins.


Web Src Content Docs Docs Openapi Typescript Plugins Orpc V1 (`web/src/content/docs/docs/openapi/typescript/plugins/orpc/v1.mdx`)


title: oRPC v1 Plugin sidebar: label: oRPC custom: version: v1

import { Tabs, TabItem } from '@astrojs/starlight/components';
import AuthorsList from '@/components/AuthorsList.astro';
import Examples from '@/components/Examples.astro';
import FeatureStatus from '@/components/FeatureStatus.astro';
import UserConfig from '@/components/UserConfig.astro';
import { stephenZhou } from '@/data/people';

oRPC

About

oRPC combines RPC with OpenAPI, allowing you to define and call remote or local procedures through a type-safe API while adhering to the OpenAPI specification.

The oRPC plugin for Hey API generates contracts from your OpenAPI spec, fully compatible with all core features.

Collaborators

Features

  • oRPC v1 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • generated contracts
  • minimal learning curve thanks to extending the underlying technology

Installation

In your configuration, add orpc to your plugins and you'll be ready to generate oRPC artifacts. 🎉

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    'orpc',
  ],
};

Output

The oRPC plugin will generate the following artifacts, depending on the input specification.

Contracts

Contracts are generated from all endpoints.

```ts title="orpc.gen.ts" import { oc } from '@orpc/contract';

const addPet = oc.route({
description: 'Add a new pet to the store.',
inputStructure: 'detailed',
method: 'POST',
operationId: 'addPet',
path: '/pet',
summary: 'Add a new pet to the store.',
tags: ['pet'],
});

text
</TabItem>
  <TabItem label="config">
```js title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    'orpc',
  ],
};

Validators

To enable schema validation, set validator to zod or one of the available validator plugins. This will automatically add the selected plugin with its default configuration.

For a more granular approach, manually add a validator plugin and set validator to the plugin name or true to automatically select a compatible plugin. Until you customize the validator plugin, both approaches will produce the same default output.

```ts {15,16} title="orpc.gen.ts" import { oc } from '@orpc/contract';

import { vAddPetBody, vAddPetResponse } from './valibot.gen';

const addPet = oc
.route({
description: 'Add a new pet to the store.',
inputStructure: 'detailed',
method: 'POST',
operationId: 'addPet',
path: '/pet',
summary: 'Add a new pet to the store.',
tags: ['pet'],
})
.input(v.object({ body: vAddPetBody }))
.output(vAddPetResponse);

text
</TabItem>
  <TabItem label="config">
```js {8,11} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'orpc',
      validator: true, // or 'valibot'
    },
    {
      name: 'valibot', // customize (optional)
      // other options
    },
  ],
};

You can choose to validate only inputs or outputs.

```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'orpc', validator: { input: 'zod', }, }, ], }; ``` ```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'orpc', validator: { output: 'zod', }, }, ], }; ```

Learn more about available validators on the Validators page.


Web Src Content Docs Docs Openapi Typescript Plugins Orpc V2 (`web/src/content/docs/docs/openapi/typescript/plugins/orpc/v2.mdx`)


title: oRPC v2 Plugin sidebar: label: oRPC custom: version: v2

import { Tabs, TabItem } from '@astrojs/starlight/components';
import AuthorsList from '@/components/AuthorsList.astro';
import Examples from '@/components/Examples.astro';
import FeatureStatus from '@/components/FeatureStatus.astro';
import UserConfig from '@/components/UserConfig.astro';
import { stephenZhou } from '@/data/people';

oRPC

About

oRPC combines RPC with OpenAPI, allowing you to define and call remote or local procedures through a type-safe API while adhering to the OpenAPI specification.

The oRPC plugin for Hey API generates contracts from your OpenAPI spec, fully compatible with all core features.

Collaborators

Features

  • oRPC v2 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • generated contracts
  • minimal learning curve thanks to extending the underlying technology

Installation

In your configuration, add orpc to your plugins and you'll be ready to generate oRPC artifacts. 🎉

js {6-9} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'orpc',
      compatibilityVersion: '2',
    },
  ],
};

Output

The oRPC plugin will generate the following artifacts, depending on the input specification.

Contracts

Contracts are generated from all endpoints.

```ts title="orpc.gen.ts" import { oc } from '@orpc/contract'; import { openapi } from '@orpc/openapi';

const addPet = oc.meta(
openapi({
description: 'Add a new pet to the store.',
inputStructure: 'detailed',
method: 'POST',
operationId: 'addPet',
path: '/pet',
summary: 'Add a new pet to the store.',
tags: ['pet'],
}),
);

text
</TabItem>
  <TabItem label="config">
```js title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'orpc',
      compatibilityVersion: '2',
    },
  ],
};

Query Styles

When generating oRPC v2 contracts, queryStyles metadata is inferred by default for OpenAPI serialization styles supported by oRPC, so arrays and delimited objects keep their original serialization behavior.

ts title="orpc.gen.ts"
const getItems = oc.meta(
  openapi({
    inputStructure: 'detailed',
    method: 'GET',
    operationId: 'getItems',
    path: '/items',
    queryStyles: {
      ids: 'array',
      filters: 'comma-delimited-object',
      tags: 'comma-delimited-array',
    },
    tags: ['items'],
  }),
);

You can disable this metadata when you want oRPC's default bracket notation.

oRPC does not currently expose a queryStyles value for form-exploded objects, so those parameters use oRPC's default bracket notation.

js {8} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      compatibilityVersion: '2',
      inferQueryStyles: false,
      name: 'orpc',
    },
  ],
};

Validators

To enable schema validation, set validator to zod or one of the available validator plugins. This will automatically add the selected plugin with its default configuration.

For a more granular approach, manually add a validator plugin and set validator to the plugin name or true to automatically select a compatible plugin. Until you customize the validator plugin, both approaches will produce the same default output.

```ts {18,19} title="orpc.gen.ts" import { oc } from '@orpc/contract'; import { openapi } from '@orpc/openapi';

import { vAddPetBody, vAddPetResponse } from './valibot.gen';

const addPet = oc
.meta(
openapi({
description: 'Add a new pet to the store.',
inputStructure: 'detailed',
method: 'POST',
operationId: 'addPet',
path: '/pet',
summary: 'Add a new pet to the store.',
tags: ['pet'],
}),
)
.input(v.object({ body: vAddPetBody }))
.output(vAddPetResponse);

text
</TabItem>
  <TabItem label="config">
```js {9,12} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      compatibilityVersion: '2',
      name: 'orpc',
      validator: true, // or 'valibot'
    },
    {
      name: 'valibot', // customize (optional)
      // other options
    },
  ],
};

You can choose to validate only inputs or outputs.

```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'orpc', validator: { input: 'zod', }, }, ], }; ``` ```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'orpc', validator: { output: 'zod', }, }, ], }; ```

Learn more about available validators on the Validators page.


Web Src Content Docs Docs Openapi Typescript Plugins Zod Mini (`web/src/content/docs/docs/openapi/typescript/plugins/zod/mini.mdx`)


title: Zod Mini Plugin custom: version: Mini versionSynonym: v4

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import UserConfig from '@/components/UserConfig.astro';

Zod

About

Zod is a TypeScript-first schema validation library with static type inference.

The Zod plugin for Hey API generates schemas from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

Features

  • Zod Mini support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • Zod schemas for requests, responses, and reusable definitions
  • minimal learning curve thanks to extending the underlying technology

Installation

In your configuration, add zod to your plugins and you'll be ready to generate Zod artifacts. 🎉

js {6-9} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'zod',
      compatibilityVersion: 'mini',
    },
  ],
};

SDKs

Zod can validate or transform data directly in your SDK. Set validator or transformer to true on the SDK plugin to enable it.

```js {10-13} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 'mini', }, { name: '@hey-api/sdk', validator: true, }, ], }; ``` ```js {10-13} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 'mini', }, { name: '@hey-api/sdk', transformer: true, }, ], }; ```

The SDK page covers validators and transformers in more detail.

Output

The Zod plugin will generate the following artifacts, depending on the input specification.

Requests

A Zod schema is generated for every request layer of each endpoint.

```ts title="zod.gen.ts" const zDeletePetHeaders = z.object({ api_key: z.optional(z.string()), });

const zDeletePetPath = z.object({
petId: z.number(),
});

const zDeletePetQuery = z.object({
additionalMetadata: z.string(),
});

text
</TabItem>
  <TabItem label="config">
```js {9} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'zod',
      compatibilityVersion: 'mini',
      requests: true,
    },
  ],
};

You can customize the naming and casing pattern for requests schemas using the .name and .case options.

Responses

A single Zod schema is generated for all endpoint's responses. If the endpoint describes multiple responses, the generated schema is a union of all possible response shapes.

```ts title="zod.gen.ts" const zResponse = z.union([ z.object({ foo: z.optional(z.string()), }), z.object({ bar: z.optional(z.number()), }), ]); ``` ```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 'mini', responses: true, }, ], }; ```

You can customize the naming and casing pattern for responses schemas using the .name and .case options.

Definitions

A Zod schema is generated for every reusable definition from your input.

```ts title="zod.gen.ts" const zFoo = z.int();

const zBar = z.object({
bar: z.optional(z.array(z.int())),
});

text
</TabItem>
  <TabItem label="config">
```js {9} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'zod',
      compatibilityVersion: 'mini',
      definitions: true,
    },
  ],
};

You can customize the naming and casing pattern for definitions schemas using the .name and .case options.

ISO Datetimes

By default, values without a timezone or with a timezone offset are not allowed in the z.iso.datetime() method.

Timezone offsets

You can allow values with timezone offsets by setting dates.offset to true.

```ts title="zod.gen.ts" export const zFoo = z.iso.datetime({ offset: true }); ``` ```js {10} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 'mini', dates: { offset: true, }, }, ], }; ```

Local times

You can allow values without a timezone by setting dates.local to true.

```ts title="zod.gen.ts" export const zFoo = z.iso.datetime({ local: true }); ``` ```js {10} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 'mini', dates: { local: true, }, }, ], }; ```

Metadata

It's often useful to associate a schema with some additional metadata for documentation, code generation, AI structured outputs, form validation, and other purposes. You can set metadata to true to attach descriptions to schemas when available.

```ts title="zod.gen.ts" export const zFoo = z.string().register(z.globalRegistry, { description: 'Additional metadata', }); ``` ```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 'mini', metadata: true, }, ], }; ```

For more control over metadata, you can provide your own function. It receives the source schema, the target node object, and the $ builder for populating metadata.

```ts title="zod.gen.ts" export const zFoo = z.string().register(z.globalRegistry, { hasTitle: true, createdAt: 1735732800000, }); ``` ```js {9-12} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 'mini', metadata({ $, node, schema }) { node.prop('hasTitle', $.literal(Boolean(schema.title))); node.prop('createdAt', $.literal(Date.now())); }, }, ], }; ```

Types

In addition to Zod schemas, you can generate schema-specific types. These can be generated for all schemas or for specific resources.

```ts title="zod.gen.ts" export type ResponseZodType = z.infer; ``` ```js {10,14} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 'mini', types: { infer: false, // by default, no `z.infer` types }, responses: { types: { infer: true, // `z.infer` types only for response schemas }, }, }, ], }; ```

You can customize the naming and casing pattern for schema-specific types using the .name and .case options.

Resolvers

You can further customize this plugin's behavior using resolvers.


Web Src Content Docs Docs Openapi Typescript Plugins Zod V3 (`web/src/content/docs/docs/openapi/typescript/plugins/zod/v3.mdx`)


title: Zod v3 Plugin custom: version: v3

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import UserConfig from '@/components/UserConfig.astro';

Zod

About

Zod is a TypeScript-first schema validation library with static type inference.

The Zod plugin for Hey API generates schemas from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

Features

  • Zod v3 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • Zod schemas for requests, responses, and reusable definitions
  • minimal learning curve thanks to extending the underlying technology

Installation

In your configuration, add zod to your plugins and you'll be ready to generate Zod artifacts. 🎉

js {6-9} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'zod',
      compatibilityVersion: 3,
    },
  ],
};

SDKs

Zod can validate or transform data directly in your SDK. Set validator or transformer to true on the SDK plugin to enable it.

```js {10-13} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 3, }, { name: '@hey-api/sdk', validator: true, }, ], }; ``` ```js {10-13} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 3, }, { name: '@hey-api/sdk', transformer: true, }, ], }; ```

The SDK page covers validators and transformers in more detail.

Output

The Zod plugin will generate the following artifacts, depending on the input specification.

Requests

A Zod schema is generated for every request layer of each endpoint.

```ts title="zod.gen.ts" const zDeletePetHeaders = z.object({ api_key: z.string().optional(), });

const zDeletePetPath = z.object({
petId: z.number(),
});

const zDeletePetQuery = z.object({
additionalMetadata: z.string(),
});

text
</TabItem>
  <TabItem label="config">
```js {9} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'zod',
      compatibilityVersion: 3,
      requests: true,
    },
  ],
};

You can customize the naming and casing pattern for requests schemas using the .name and .case options.

Responses

A single Zod schema is generated for all endpoint's responses. If the endpoint describes multiple responses, the generated schema is a union of all possible response shapes.

```ts title="zod.gen.ts" const zResponse = z.union([ z.object({ foo: z.string().optional(), }), z.object({ bar: z.number().optional(), }), ]); ``` ```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 3, responses: true, }, ], }; ```

You can customize the naming and casing pattern for responses schemas using the .name and .case options.

Definitions

A Zod schema is generated for every reusable definition from your input.

```ts title="zod.gen.ts" const zFoo = z.number().int();

const zBar = z.object({
bar: z.array(z.number().int()).optional(),
});

text
</TabItem>
  <TabItem label="config">
```js {9} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'zod',
      compatibilityVersion: 3,
      definitions: true,
    },
  ],
};

You can customize the naming and casing pattern for definitions schemas using the .name and .case options.

ISO Datetimes

By default, values without a timezone or with a timezone offset are not allowed in the z.string().datetime() method.

Timezone offsets

You can allow values with timezone offsets by setting dates.offset to true.

```ts title="zod.gen.ts" export const zFoo = z.string().datetime({ offset: true }); ``` ```js {10} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 3, dates: { offset: true, }, }, ], }; ```

Local times

You can allow values without a timezone by setting dates.local to true.

```ts title="zod.gen.ts" export const zFoo = z.string().datetime({ local: true }); ``` ```js {10} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 3, dates: { local: true, }, }, ], }; ```

Metadata

It's often useful to associate a schema with some additional metadata for documentation, code generation, AI structured outputs, form validation, and other purposes. If this is your use case, you can set metadata to true to generate additional metadata about schemas.

```ts title="zod.gen.ts" export const zFoo = z.string().describe('Additional metadata'); ``` ```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 3, metadata: true, }, ], }; ```

Types

In addition to Zod schemas, you can generate schema-specific types. These can be generated for all schemas or for specific resources.

```ts title="zod.gen.ts" export type ResponseZodType = z.infer; ``` ```js {10,14} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', compatibilityVersion: 3, types: { infer: false, // by default, no `z.infer` types }, responses: { types: { infer: true, // `z.infer` types only for response schemas }, }, }, ], }; ```

You can customize the naming and casing pattern for schema-specific types using the .name and .case options.

Resolvers

You can further customize this plugin's behavior using resolvers.


Web Src Content Docs Docs Openapi Typescript Plugins Zod V4 (`web/src/content/docs/docs/openapi/typescript/plugins/zod/v4.mdx`)


title: Zod v4 Plugin sidebar: label: Zod custom: version: v4

import { Tabs, TabItem } from '@astrojs/starlight/components';
import Examples from '@/components/Examples.astro';
import UserConfig from '@/components/UserConfig.astro';

Zod

About

Zod is a TypeScript-first schema validation library with static type inference.

The Zod plugin for Hey API generates schemas from your OpenAPI spec, fully compatible with validators, transformers, and all core features.

Features

  • Zod v4 support
  • seamless integration with @hey-api/openapi-ts ecosystem
  • Zod schemas for requests, responses, and reusable definitions
  • minimal learning curve thanks to extending the underlying technology

Installation

In your configuration, add zod to your plugins and you'll be ready to generate Zod artifacts. 🎉

js {6} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    'zod',
  ],
};

SDKs

Zod can validate or transform data directly in your SDK. Set validator or transformer to true on the SDK plugin to enable it.

```js {7-10} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins 'zod', { name: '@hey-api/sdk', validator: true, }, ], }; ``` ```js {7-10} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins 'zod', { name: '@hey-api/sdk', transformer: true, }, ], }; ```

The SDK page covers validators and transformers in more detail.

Output

The Zod plugin will generate the following artifacts, depending on the input specification.

Requests

A Zod schema is generated for every request layer of each endpoint.

```ts title="zod.gen.ts" const zDeletePetHeaders = z.object({ api_key: z.string().optional(), });

const zDeletePetPath = z.object({
petId: z.number(),
});

const zDeletePetQuery = z.object({
additionalMetadata: z.string(),
});

text
</TabItem>
  <TabItem label="config">
```js {8} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'zod',
      requests: true,
    },
  ],
};

You can customize the naming and casing pattern for requests schemas using the .name and .case options.

Responses

A single Zod schema is generated for all endpoint's responses. If the endpoint describes multiple responses, the generated schema is a union of all possible response shapes.

```ts title="zod.gen.ts" const zResponse = z.union([ z.object({ foo: z.optional(z.string()), }), z.object({ bar: z.optional(z.number()), }), ]); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', responses: true, }, ], }; ```

You can customize the naming and casing pattern for responses schemas using the .name and .case options.

Definitions

A Zod schema is generated for every reusable definition from your input.

```ts title="zod.gen.ts" const zFoo = z.int();

const zBar = z.object({
bar: z.optional(z.array(z.int())),
});

text
</TabItem>
  <TabItem label="config">
```js {8} title="openapi-ts.config.ts"
export default {
  input: 'hey-api/backend', // sign up at app.heyapi.dev
  output: 'src/client',
  plugins: [
    // ...other plugins
    {
      name: 'zod',
      definitions: true,
    },
  ],
};

You can customize the naming and casing pattern for definitions schemas using the .name and .case options.

ISO Datetimes

By default, values without a timezone or with a timezone offset are not allowed in the z.iso.datetime() method.

Timezone offsets

You can allow values with timezone offsets by setting dates.offset to true.

```ts title="zod.gen.ts" export const zFoo = z.iso.datetime({ offset: true }); ``` ```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', dates: { offset: true, }, }, ], }; ```

Local times

You can allow values without a timezone by setting dates.local to true.

```ts title="zod.gen.ts" export const zFoo = z.iso.datetime({ local: true }); ``` ```js {9} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', dates: { local: true, }, }, ], }; ```

Metadata

It's often useful to associate a schema with some additional metadata for documentation, code generation, AI structured outputs, form validation, and other purposes. You can set metadata to true to attach descriptions to schemas when available.

```ts title="zod.gen.ts" export const zFoo = z.string().register(z.globalRegistry, { description: 'Additional metadata', }); ``` ```js {8} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', metadata: true, }, ], }; ```

For more control over metadata, you can provide your own function. It receives the source schema, the target node object, and the $ builder for populating metadata.

```ts title="zod.gen.ts" export const zFoo = z.string().register(z.globalRegistry, { hasTitle: true, createdAt: 1735732800000, }); ``` ```js {8-11} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', metadata({ $, node, schema }) { node.prop('hasTitle', $.literal(Boolean(schema.title))); node.prop('createdAt', $.literal(Date.now())); }, }, ], }; ```

Types

In addition to Zod schemas, you can generate schema-specific types. These can be generated for all schemas or for specific resources.

```ts title="zod.gen.ts" export type ResponseZodType = z.infer; ``` ```js {9,13} title="openapi-ts.config.ts" export default { input: 'hey-api/backend', // sign up at app.heyapi.dev output: 'src/client', plugins: [ // ...other plugins { name: 'zod', types: { infer: false, // by default, no `z.infer` types }, responses: { types: { infer: true, // `z.infer` types only for response schemas }, }, }, ], }; ```

You can customize the naming and casing pattern for schema-specific types using the .name and .case options.

Resolvers

You can further customize this plugin's behavior using resolvers.


--- METRICS ---