openapi-ts-json-schema
v2.3.0
Published
Generate TypeScript-first JSON schemas from OpenAPI definitions
Maintainers
Readme
openapi-ts-json-schema
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.
Why?
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.
Example
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 }
}How this fits with openapi-typescript
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
Installation
npm i openapi-ts-json-schema -DUsage
import { openapiToTsJsonSchema } from 'openapi-ts-json-schema';
const { outputPath } = await openapiToTsJsonSchema({
openApiDocument: 'path/to/open-api-specs.yaml',
targets: {
collections: ['components.schemas', 'paths'],
},
});Schemas are generated in a folder mirroring your OpenAPI layout (default: schemas-autogenerated).
CommonJS
Since openapi-ts-json-schema is distributed as an ESM-only package, it must be imported using a dynamic import() when used from a CommonJS project:
async function run() {
const { openapiToTsJsonSchema } = await import('openapi-ts-json-schema');
const { outputPath } = await openapiToTsJsonSchema({
openApiDocument: 'path/to/open-api-specs.yaml',
targets: {
collections: ['components.schemas', 'paths'],
},
importExtension: 'none',
});
}
run();Options
Core options
| 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" |
| moduleSystem (deprecated) | "cjs" \| "esm" | Deprecated — use importExtension instead. Kept for backwards compatibility: "esm" maps to importExtension: "js", "cjs" maps to importExtension: "none". Ignored when importExtension is also provided. | - |
| silent | boolean | Don't log user messages. | false |
Advanced options
| 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. | - |
Full configuration example
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()],
});refHandling option
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.
idMapper option
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'Return values
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
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.
How it works
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.
Todo
- 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
