UNPKG

openapi-ts-json-schema

Version:

Generate TypeScript-first JSON schemas from OpenAPI definitions

261 lines (199 loc) 14.2 kB
# openapi-ts-json-schema [![Build Status][ci-badge]][ci] [![Npm version][npm-version-badge]][npm] [![Coveralls][coveralls-badge]][coveralls] Generate **TypeScript-first JSON Schemas** (`.ts` modules with `as const`) directly from your OpenAPI definitions — so you can use the same schema for both **runtime validation** and **TypeScript type inference**. ## Why? Keeping **OpenAPI specs**, **runtime validators**, and **TypeScript types** in sync is hard. Many teams end up maintaining the same api models in different formats: - JSON Schema for runtime validation (`Ajv`, `Fastify`, etc.) - TypeScript types for static checking `openapi-ts-json-schema` solves this by generating **TypeScript JSON Schemas directly from your OpenAPI definitions**: valid JSON schemas written as **TypeScript modules**, ready for runtime validation and type inference. These schemas: - ✅ are 100% JSON Schema–compatible (usable with `Ajv`, `Fastify`, etc.) - ✅ are TypeScript-native (`as const` objects you can import) - ✅ can be used for type inference via [json-schema-to-ts](https://github.com/ThomasAribart/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: ```yaml components: schemas: User: type: object properties: id: { type: string } name: { type: string } required: [id, name] ``` You get this TypeScript JSON schema: ```ts // 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: ```ts 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 } } else { console.error(validate.errors); } ``` ## Installation ``` npm i openapi-ts-json-schema -D ``` ## Usage ```ts 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: ```ts 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'], }, moduleSystem: 'cjs', }); } run(); ``` ## Options ### Core options | Property | Type&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | Description | Default | | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | | **openApiDocument** _(required)_ | `string` | Path to an OpenAPI document (JSON or YAML). | - | | **targets** _(required)_ | `{`</br>` collections?: string[];`</br>` single?: string[];`</br>`}` | OpenAPI definition paths to generate JSON Schemas from _(dot notation)_.<br/><br/>`collections`: paths pointing to objects/records of definitions, where each entry will be generated (eg: `["components.schemas"]`).<br/><br/>`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.<br/>`"inline"`: inline `$ref` schemas.<br/>`"keep"`: keep `$ref` values. | `"import"` | | **moduleSystem** | `"cjs" \| "esm"` | Controls how import specifiers are written in generated artifacts. Configure this option based on whether the consuming project is using CommonJS or ECMAScript modules. | `"esm"` | | **silent** | `boolean` | Don't log user messages. | `false` | ### Advanced options | Property | Type | Description | Default | | ----------------- | ------------------------------------------ | ----------------------------------------------------------------------------------------------------- | ------- | | **idMapper** | `(params: { id: string }) => string` | Customize generated schemas `$id`s and `$ref`s values. Useful for enforcing naming conventions. | - | | **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](./docs/plugins.md). | - | ### Full configuration example ```ts 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', moduleSystem: 'esm', 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 `$ref`s are resolved: | `refHandling` option | description | | -------------------- | ---------------------------------------------------------------------------------------------- | | `inline` | Inlines `$refs`s, creating self-contained schemas (no imports, but possible redundancy). | | `import` | Replaces`$ref`s with imports of the target definition | | `keep` | Leaves `$ref`s untouched — useful if you plan to interpret `$ref`s dynamically or use a plugin | Circular references are supported: - `inline`: circular refs are replaced with `{}` - `import`: resolves the JSON schema but TypeScript recursion halts (`any` type, TS error 7022) - `keep`: circular refs left unresolved See [tests](https://github.com/toomuchdesign/openapi-ts-json-schema/blob/master/test/circularReference.test.ts) for details. ## Return values Along with generated schema files, `openapi-ts-json-schema` returns metadata: ```ts { // 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 Extend `openapi-ts-json-schema` with custom generators. Currently available plugins: - `generateSchemaWith$idPlugin` - `fastifyIntegrationPlugin` See [plugins documentation 📖](./docs/plugins.md). ## How it works Given an OpenAPI definition file, `openapi-ts-json-schema`: - Resolves and dereferences $refs (using [@apidevtools/json-schema-ref-parser](https://github.com/APIDevTools/json-schema-ref-parser)) - Converts OpenAPI objects to JSON Schema (via [@openapi-contrib/openapi-schema-to-json-schema](https://github.com/APIDevTools/json-schema-ref-parser) & [`openapi-jsonschema-parameters`](https://www.npmjs.com/package/openapi-jsonschema-parameters)) - Generates `.ts` files exporting each schema as `as const` - Mirrors the original OpenAPI structure in the generated folder - Supports plugins (e.g. for Fastify integration) Take a look at the [Developer's notes](./docs/developer-notes.md) for a few more in-depth explanations. ## Todo - Improve external `#ref`s handling (currently being inlined and duplicated) - Find a way to merge multiple different OpenApi definitions consistently - Consider implementing an option to inline circular `$ref`s with a configurable nesting level [ci-badge]: https://github.com/toomuchdesign/openapi-ts-json-schema/actions/workflows/ci.yml/badge.svg [ci]: https://github.com/toomuchdesign/openapi-ts-json-schema/actions/workflows/ci.yml [coveralls-badge]: https://coveralls.io/repos/github/toomuchdesign/openapi-ts-json-schema/badge.svg?branch=master [coveralls]: https://coveralls.io/github/toomuchdesign/openapi-ts-json-schema?branch=master [npm]: https://www.npmjs.com/package/openapi-ts-json-schema [npm-version-badge]: https://img.shields.io/npm/v/openapi-ts-json-schema.svg