json-schema-to-openapi
v1.0.0
Published
Zero-dependency converter from JSON Schema to OpenAPI 3.0 and 3.1 schema objects — nullable, refs, definitions, const, examples, document builders
Maintainers
Readme
json-schema-to-openapi
Zero-dependency converter from JSON Schema (draft-07 / 2019-09 / 2020-12) to OpenAPI 3.0 and OpenAPI 3.1 schema objects.
Handles nullable, $ref remapping, definitions → components, const, examples, and includes document / operation builders.
npm install json-schema-to-openapiQuick start
import { toOpenApi30, toOpenApi31, buildOpenApiDoc, definitionsToComponents30 } from 'json-schema-to-openapi';
const jsonSchema = {
$schema: 'http://json-schema.org/draft-07/schema#',
type: ['string', 'null'],
minLength: 1,
examples: ['hello', 'world'],
const: 'fixed',
};
toOpenApi30(jsonSchema);
// {
// type: 'string',
// nullable: true,
// minLength: 1,
// example: 'hello',
// enum: ['fixed'],
// }
toOpenApi31({ type: 'string', nullable: true });
// { type: ['string', 'null'] }toOpenApi30(schema, opts?)
Deep-converts a JSON Schema object to an OpenAPI 3.0 Schema Object.
| Transformation | Input | Output |
|---|---|---|
| Nullable type union | type: ['string', 'null'] | { type: 'string', nullable: true } |
| Null-only type | type: ['null'] | { nullable: true } |
| Multi-type array | type: ['string', 'integer'] | oneOf: [{type:'string'},{type:'integer'}] |
| Const value | const: 42 | enum: [42] |
| Examples array | examples: ['a', 'b'] | example: 'a' |
| Definitions refs | #/definitions/Foo | #/components/schemas/Foo |
| $defs refs | #/$defs/Foo | #/components/schemas/Foo |
| Strip unsupported | $schema, $id, if/then/else, contentEncoding, … | (removed) |
| Pattern props | patternProperties | x-patternProperties |
import { toOpenApi30 } from 'json-schema-to-openapi';
const oas = toOpenApi30({
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: ['string', 'null'], examples: ['Alice'] },
role: { const: 'admin' },
},
required: ['id'],
});
/*
{
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string', nullable: true, example: 'Alice' },
role: { enum: ['admin'] },
},
required: ['id'],
}
*/Options:
toOpenApi30(schema, {
keepRefs: false, // skip $ref remapping
refMap: {}, // custom { '#/old': '#/new' } overrides
strictMode: false, // throw on if/then/else
allowAdditional: false, // keep x-* extensions
})toOpenApi31(schema, opts?)
Converts to an OpenAPI 3.1 Schema Object (aligned with JSON Schema 2020-12).
| Transformation | Input | Output |
|---|---|---|
| OAS 3.0 nullable | nullable: true | appends 'null' to type |
| x-nullable | x-nullable: true | same as above |
| Definitions refs | #/definitions/Foo | #/components/schemas/Foo |
| Strip meta | $schema, $comment | (removed) |
| Keep | const, if/then/else, examples array, $ref + siblings | (kept as-is) |
import { toOpenApi31 } from 'json-schema-to-openapi';
toOpenApi31({ type: 'string', nullable: true });
// { type: ['string', 'null'] }
toOpenApi31({ $schema: '...', type: 'object', if: { required: ['x'] }, then: { properties: { x: { type: 'string' } } } });
// { type: 'object', if: { required: ['x'] }, then: { properties: { x: { type: 'string' } } } }Ref utilities
remapRef(ref, map?)
import { remapRef } from 'json-schema-to-openapi';
remapRef('#/definitions/User') // '#/components/schemas/User'
remapRef('#/$defs/User') // '#/components/schemas/User'
remapRef('#/other/X', { '#/other/X': '#/components/schemas/X' }) // '#/components/schemas/X'convertRefs(schema, fn)
Walk every $ref in the tree and replace it:
import { convertRefs } from 'json-schema-to-openapi';
const out = convertRefs(schema, ref => ref.replace('#/definitions/', '#/components/schemas/'));refsToComponents(schema)
Convenience wrapper — remaps #/definitions/X and #/$defs/X to #/components/schemas/X everywhere:
import { refsToComponents } from 'json-schema-to-openapi';
refsToComponents(schema);Definition helpers
extractDefinitions(schema)
Pull out definitions or $defs into a flat map:
import { extractDefinitions } from 'json-schema-to-openapi';
const defs = extractDefinitions({
$defs: { User: { type: 'object' } },
definitions: { Tag: { type: 'string' } },
});
// { User: { type: 'object' }, Tag: { type: 'string' } }definitionsToComponents30(defs, opts?) / definitionsToComponents31(defs, opts?)
Convert a definitions map into a components.schemas-ready object:
import { extractDefinitions, definitionsToComponents30, buildOpenApiDoc } from 'json-schema-to-openapi';
const defs = extractDefinitions(rootSchema);
const schemas = definitionsToComponents30(defs);
const doc = buildOpenApiDoc({ title: 'My API', schemas });Schema analysis
import { isNullable, getTypes, hasRef, collectRefs } from 'json-schema-to-openapi';
isNullable({ type: ['string', 'null'] }) // true
isNullable({ nullable: true }) // true
isNullable({ enum: [null, 'a'] }) // true
isNullable({ type: 'string' }) // false
getTypes({ type: 'string' }) // ['string']
getTypes({ type: ['string', 'null'] }) // ['string', 'null']
getTypes({ enum: [1, 2] }) // []
hasRef({ properties: { a: { $ref: '#/components/schemas/Foo' } } }) // true
collectRefs(schema) // ['#/components/schemas/Foo', '#/components/schemas/Bar']Schema utilities
import { stripMeta, mergeSchemas, unwrapSingleAllOf, makePartial, pickProperties, omitProperties } from 'json-schema-to-openapi';
// Remove $schema / $id / $comment from the root
stripMeta({ $schema: '...', type: 'string' })
// { type: 'string' }
// Merge two schemas (properties + required are merged)
mergeSchemas([
{ type: 'object', properties: { a: { type: 'string' } }, required: ['a'] },
{ properties: { b: { type: 'number' } } },
])
// { type: 'object', properties: { a: {...}, b: {...} }, required: ['a'] }
// Unwrap { allOf: [schema] } with no other keys
unwrapSingleAllOf({ allOf: [{ type: 'string' }] })
// { type: 'string' }
// Remove required array (all props optional)
makePartial({ type: 'object', required: ['id'], properties: { id: {}, name: {} } })
// Keep only selected properties
pickProperties(schema, ['id', 'name'])
// Remove selected properties
omitProperties(schema, ['internalField'])OpenAPI document builders
buildOpenApiDoc(opts)
Scaffold a full OAS 3.0 document:
import { buildOpenApiDoc } from 'json-schema-to-openapi';
const doc = buildOpenApiDoc({
title: 'Pet Store API',
version: '2.0.0',
description: 'A sample API',
servers: [{ url: 'https://api.example.com/v2' }],
schemas: { Pet: petSchema, Error: errorSchema },
});addComponentSchemas(doc, schemas)
Add schemas into an existing document's components.schemas:
import { addComponentSchemas } from 'json-schema-to-openapi';
addComponentSchemas(doc, { NewModel: { type: 'object' } });buildOperation(opts)
import { buildOperation, buildRequestBody, buildResponse, buildErrorResponses } from 'json-schema-to-openapi';
const op = buildOperation({
summary: 'Create a pet',
operationId: 'createPet',
tags: ['pets'],
requestBody: buildRequestBody({ $ref: '#/components/schemas/NewPet' }),
responses: {
'201': buildResponse({ description: 'Pet created', schema: { $ref: '#/components/schemas/Pet' } }),
...buildErrorResponses([400, 422, 500]),
},
});buildParameter(opts)
import { buildParameter } from 'json-schema-to-openapi';
buildParameter({ name: 'petId', in: 'path', schema: { type: 'integer' } })
buildParameter({ name: 'limit', in: 'query', schema: { type: 'integer', default: 20 }, required: false })buildRequestBody(schema, opts?)
buildRequestBody({ $ref: '#/components/schemas/NewPet' })
buildRequestBody(schema, { description: 'Pet data', required: true, mediaType: 'application/json' })buildResponse(opts?)
buildResponse({ description: 'OK', schema: { $ref: '#/components/schemas/Pet' } })
buildResponse({ description: 'No Content' })buildErrorResponses(codes?)
buildErrorResponses() // 400, 401, 403, 404, 500
buildErrorResponses([422, 500]) // custom setCommonJS
const { toOpenApi30, toOpenApi31, buildOpenApiDoc } = require('json-schema-to-openapi');License
MIT
