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 withpatch.
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
WARNING
The validator feature is very limited. You can help improve it by submitting moreuse 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, setvalidate_EXPERIMENTAL totrue.
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
Setinclude to match operations to be included orexclude to match operations to be excluded. Both exact keys and regular expressions are supported. When both rules match the same operation,exclude takes precedence overinclude.
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//'], }, }, },};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
Setinclude to match tags to be included orexclude to match tags to be excluded. When both rules match the same tag,exclude takes precedence overinclude.
export default { input:'hey-api/backend',// sign up at app.heyapi.dev output:'src/client', parser: { filters: { tags: { include: ['v2'], }, }, },};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 settingdeprecated tofalse.
export default { input:'hey-api/backend',// sign up at app.heyapi.dev output:'src/client', parser: { filters: { deprecated:false, }, },};Schemas
Setinclude to match schemas to be included orexclude to match schemas to be excluded. Both exact keys and regular expressions are supported. When both rules match the same schema,exclude takes precedence overinclude.
export default { input:'hey-api/backend',// sign up at app.heyapi.dev output:'src/client', parser: { filters: { schemas: { include: ['Foo','/^Bar/'], }, }, },};export default { input:'hey-api/backend',// sign up at app.heyapi.dev output:'src/client', parser: { filters: { schemas: { exclude: ['Foo','/^Bar/'], }, }, },};Parameters
Setinclude to match parameters to be included orexclude to match parameters to be excluded. Both exact keys and regular expressions are supported. When both rules match the same parameter,exclude takes precedence overinclude.
export default { input:'hey-api/backend',// sign up at app.heyapi.dev output:'src/client', parser: { filters: { parameters: { include: ['QueryParameter','/^MyQueryParameter/'], }, }, },};export default { input:'hey-api/backend',// sign up at app.heyapi.dev output:'src/client', parser: { filters: { parameters: { exclude: ['QueryParameter','/^MyQueryParameter/'], }, }, },};Request Bodies
Setinclude to match request bodies to be included orexclude 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 overinclude.
export default { input:'hey-api/backend',// sign up at app.heyapi.dev output:'src/client', parser: { filters: { requestBodies: { include: ['Payload','/^SpecialPayload/'], }, }, },};export default { input:'hey-api/backend',// sign up at app.heyapi.dev output:'src/client', parser: { filters: { requestBodies: { exclude: ['Payload','/^SpecialPayload/'], }, }, },};Responses
Setinclude to match responses to be included orexclude to match responses to be excluded. Both exact keys and regular expressions are supported. When both rules match the same response,exclude takes precedence overinclude.
export default { input:'hey-api/backend',// sign up at app.heyapi.dev output:'src/client', parser: { filters: { responses: { include: ['Foo','/^Bar/'], }, }, },};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, setorphans tofalse. This is the default value when combined with any other filters. If this isn't the desired behavior, you may want to setorphans totrue to always preserve unused resources.
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, setpreserveOrder totrue.
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 deterministicpatches. 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.
export default { input:'hey-api/backend',// sign up at app.heyapi.dev output:'src/client', parser: { transforms: { enums:'root', }, },};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 forenums schemas using the.name and.case options.
Properties required by default
By default, any object schema with a missingrequired keyword is interpreted as "no properties are required." This is the correct behavior according to the OpenAPI standard. However, some specifications interpret a missingrequired 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.
export default { input:'hey-api/backend',// sign up at app.heyapi.dev output:'src/client', parser: { transforms: { propertiesRequiredByDefault:false, }, },};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.
export default { input:'hey-api/backend',// sign up at app.heyapi.dev output:'src/client', parser: { transforms: { readWrite: { requests:'{{name}}Writable', responses:'{{name}}', }, }, },};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 forrequests andresponses 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.
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; }, }, },};export default { input:'hey-api/backend',// sign up at app.heyapi.dev output:'src/client', parser: { transforms: { schemaName:'Api{{name}}', }, },};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, andstart.
You can provide custom pagination keywords usingpagination.keywords.
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', ], }, },};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 viahooks or per plugin through the~hooks property.
export default { input:'hey-api/backend',// sign up at app.heyapi.dev output:'src/client', parser: { hooks: {},// configure global hooks here },};export default { input:'hey-api/backend',// sign up at app.heyapi.dev output:'src/client', plugins: [ { name:'@tanstack/react-query', '~hooks': {},// configure plugin hooks here }, ],};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 includequery,mutation, both, or none. Plugins may use these values to decide whether to generate specific output. For example, you usually don't want to generateTanStack Query options for PATCH operations.
Query operations
By default, GET operations are classified asquery operations.
Mutation operations
By default, DELETE, PATCH, POST, and PUT operations are classified asmutation operations.
Example: POST search query
Imagine your API has a POST/search endpoint that accepts a large payload. By default, it's classified as amutation, but in practice it behaves like aquery, and yourstate management plugin should generate query hooks.
You can achieve this by classifying the operation asquery in a matcher.
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; } }, }, }, },};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 namedFoo should end up in the filef.ts.
You can achieve this by using the symbol's name.
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(); } }, }, }, },};Examples
You can view live examples onStackBlitz.
Sponsors
Hey API is sponsor-funded. If you rely on Hey API in production, consider becoming asponsor to accelerate the roadmap.
Gold

Best-in-class developer interfaces for your API.
stainless.comThe open source coding agent.
opencode.aiThe intelligent knowledge platform.
mintlify.com
