Generate TypeScript JSON Schema modules (.ts files with as const) directly from your OpenAPI spec — one source of truth for runtime validation and static type inference.
Your OpenAPI spec, runtime schemas, and TypeScript types tend to drift when maintained separately. This library generates them all from the spec — one source of truth.
The output is plain JSON Schema as const TypeScript modules, so they work with any JSON Schema-aware tool (Ajv, TypeBox, Hyperjump, Fastify, …) and can be passed directly to json-schema-to-ts for static type inference.
These schemas:
- ✅ are 100% JSON Schema–compatible (usable with
Ajv,Fastify, etc.) - ✅ are TypeScript-native (
as constobjects you can import) - ✅ can be used for type inference via json-schema-to-ts
- ✅ are generated automatically from your OpenAPI spec
In short: OpenAPI spec becomes the single source of truth for both runtime validation and TypeScript typing.
Important
Why a code generator and not just a JSON import?
The whole reason this library exists is that TypeScript cannot import a .json file as const (microsoft/TypeScript#32063). Imported JSON is widened to its base types (string, number, …), which destroys the literal information json-schema-to-ts needs to infer types from a schema.
The workaround is to emit .ts modules with an as const assertion — exactly what openapi-ts-json-schema does. If TypeScript ever supports import schema from './schema.json' as const, most use cases for this library go away and you can consume your OpenAPI-derived JSON Schemas directly.
From this OpenAPI definition:
components:
schemas:
User:
type: object
properties:
id: { type: string }
name: { type: string }
required: [id, name]openapi-ts-json-schema generates:
// components/schemas/User.ts
export default {
type: 'object',
properties: {
id: { type: 'string' },
name: { type: 'string' },
},
required: ['id', 'name'],
} as const;Now you can use it for both runtime validation and type inference:
import Ajv from 'ajv';
import type { FromSchema } from 'json-schema-to-ts';
import userSchema from './components/schemas/User';
const ajv = new Ajv();
const validate = ajv.compile<FromSchema<typeof userSchema>>(userSchema);
const data: unknown = {};
if (validate(data)) {
// data is now typed as { id: string; name: string }
}Different tools for different jobs — not competitors:
- Types only, no runtime →
openapi-typescript - Runtime schemas + types from one spec →
openapi-ts-json-schema+json-schema-to-ts(works with any JSON Schema-compatible tool) - Both together →
openapi-ts-json-schemahandles runtime; fall back toopenapi-typescripttypes wherejson-schema-to-tsinference breaks down
npm i openapi-ts-json-schema -D
openapi-ts-json-schema can be invoked through its CLI or used as a library. Both surfaces share the same options.
The package ships with an openapi-ts-json-schema binary supporting two mutually exclusive modes: passing options as flags, or pointing to a config file.
For simple setups, pass options directly as flags. Flags are a serialized projection of the Options object — see that section for full semantics.
npx openapi-ts-json-schema \
--input path/to/open-api-specs.yaml \
--collections paths| Flag | Maps to |
|---|---|
--input <file> |
openApiDocument |
--collections <paths> |
targets.collections (comma-separated) |
--single <paths> |
targets.single (comma-separated) |
--output <dir> |
outputPath |
--import-extension <ext> |
importExtension |
--ref-handling <strategy> |
refHandling |
--silent |
silent |
For setups that need schemaPatcher, idMapper, or plugins — none of which can be expressed as CLI flags — pass a JS or TS config file via --config. The file must export default an Options object. When --config is used, no other flags are allowed.
npx openapi-ts-json-schema --config ./openapi-ts-json-schema.config.ts// openapi-ts-json-schema.config.ts
import type { Options } from 'openapi-ts-json-schema';
import { fastifyIntegrationPlugin } from 'openapi-ts-json-schema';
const config: Options = {
openApiDocument: 'path/to/open-api-specs.yaml',
targets: {
collections: ['paths'],
},
plugins: [fastifyIntegrationPlugin()],
};
export default config;import { openapiToTsJsonSchema } from 'openapi-ts-json-schema';
const { outputPath } = await openapiToTsJsonSchema({
openApiDocument: 'path/to/open-api-specs.yaml',
targets: {
collections: ['paths'],
},
});| Property | Type | Description | Default |
|---|---|---|---|
| openApiDocument (required) | string |
Path to an OpenAPI document (JSON or YAML). | - |
| targets (required) | { collections?: string[]; single?: string[];} |
OpenAPI definition paths to generate JSON Schemas from (dot notation).collections: paths pointing to objects/records of definitions, where each entry will be generated (eg: ["components.schemas"]).single: paths pointing to individual definitions to generate (eg: ["paths./users/{id}"]). |
- |
| outputPath | string |
Directory where generated schemas will be written. Defaults to /schemas-autogenerated in the same directory of openApiDocument. |
- |
| refHandling | "import" | "inline" | "keep" |
"import": generate and import $ref schemas."inline": inline $ref schemas."keep": keep $ref values. |
"import" |
| importExtension | "js" | "ts" | "none" |
File extension appended to relative import specifiers in generated artifacts. | "js" |
| silent | boolean |
Don't log user messages. | false |
| Property | Type | Description | Default |
|---|---|---|---|
| idMapper | (params: { id: string }) => string |
Map internal schema ids to custom $id and $ref values in the generated output. See idMapper option below. |
- |
| schemaPatcher | (params: { schema: JSONSchema }) => void |
Hook called for every generated schema node, allowing programmatic mutation before output. | - |
| plugins | ReturnType<Plugin>[] |
List of plugins to extend or customize the generation process. See plugins docs. | - |
import {
generateSchemaWith,
openapiToTsJsonSchema,
} from 'openapi-ts-json-schema';
await openapiToTsJsonSchema({
openApiDocument: './openapi.yaml',
targets: {
collections: ['components.schemas'],
single: ['paths./users/{id}'],
},
outputPath: './generated',
refHandling: 'import',
importExtension: 'js',
silent: false,
idMapper: ({ id }) => id.toUpperCase(),
schemaPatcher: ({ schema }) => {
if (schema.properties && !schema.type) {
schema.type = 'object';
}
},
plugins: [generateSchemaWith$idPlugin()],
});Three strategies for how $refs are resolved:
refHandling option |
description |
|---|---|
inline |
Inlines $refss, creating self-contained schemas (no imports, but possible redundancy). |
import |
Replaces$refs with imports of the target definition |
keep |
Leaves $refs untouched — useful if you plan to interpret $refs dynamically or use a plugin |
Circular references are supported:
inline: circular refs are replaced with{}import: resolves the JSON schema but TypeScript recursion halts (anytype, TS error 7022)keep: circular refs left unresolved
See tests for details.
Every schema gets an internal id derived from its path in the OpenAPI document (e.g. /components/schemas/Pet). idMapper lets you rewrite these ids before they are written as $id and $ref values in the generated output.
Strip the /components/schemas/ prefix so Fastify's addSchema / getSchema can look up schemas by a short name:
await openapiToTsJsonSchema({
openApiDocument: './openapi.yaml',
targets: { collections: ['components.schemas'] },
refHandling: 'keep',
idMapper: ({ id }) => {
const prefix = '/components/schemas/';
return id.startsWith(prefix) ? id.slice(prefix.length) : id;
// '/components/schemas/Pet' → 'Pet'
},
});Map to a flat, namespaced identifier across multiple target collections:
idMapper: ({ id }) => id.replaceAll('/', '.').replace(/^\./, ''),
// '/components/schemas/Pet' → 'components.schemas.Pet'
// '/paths/~1users~1{id}/get' → 'paths.~1users~1{id}.get'Along with generated schema files, openapi-ts-json-schema returns metadata:
{
// The path where the schemas are generated
outputPath: string;
metaData: {
// Meta data of the generated schemas
schemas: Map<
// Schema internal id. Eg: "/components/schemas/MySchema"
string,
{
id: string;
// Internal unique schema identifier. Eg `"/components/schemas/MySchema"`
$id: string;
// JSON schema Compound Schema Document `$id`. Eg: `"/components/schemas/MySchema"`
uniqueName: string;
// Unique JavaScript identifier used as import name. Eg: `"componentsSchemasMySchema"`
openApiDefinition: OpenApiObject;
// Original dereferenced openAPI definition
originalSchema: JSONSchema | string;
// Original dereferenced JSON schema
isRef: boolean;
// True if schemas is used as a `$ref`
shouldBeGenerated: boolean;
// Text content of schema file
fileContent?: string;
absoluteDirName: string;
// Absolute path pointing to schema folder (posix or win32). Eg: `"Users/username/output/path/components/schemas"`
absolutePath: string;
// Absolute path pointing to schema file (posix or win32). Eg: `"Users/username/output/path/components/schemas/MySchema.ts"`
absoluteImportPath: string;
// Absolute import path (posix or win32, without extension). Eg: `"Users/username/output/path/components/schemas/MySchema"`
}
>;
}
}Plugins generate extra artifacts using the same internal metadata. Available plugins:
generateSchemaWith$idPlugin— adds a named export with the schema$idfield set.fastifyIntegrationPlugin— generates afastify-integration.tsfile with aschemasarray forfastify.addSchemaand aRefSchemastype for thejson-schema-to-tstype provider.
Given an OpenAPI definition file, openapi-ts-json-schema:
- Resolves and dereferences $refs (using @apidevtools/json-schema-ref-parser)
- Converts OpenAPI objects to JSON Schema (via @openapi-contrib/openapi-schema-to-json-schema &
openapi-jsonschema-parameters) - Generates
.tsfiles exporting each schema asas const - Mirrors the original OpenAPI structure in the generated folder
- Runs plugins before and after file generation
See developer's notes.
- Improve external
#refs handling (currently being inlined and duplicated) - Find a way to merge multiple different OpenApi definitions consistently
- Consider implementing an option to inline circular
$refs with a configurable nesting level