Skip to content
Effect Days 2026 Get your ticket

JsonSchema

Helpers for normalizing and converting JSON Schema and OpenAPI schema documents. Supported inputs include JSON Schema Draft-07, Draft 2020-12, OpenAPI 3.0, and OpenAPI 3.1; conversions normalize through Document<"draft-2020-12"> before emitting another dialect, including JSON Schema Draft-04. The module also defines document types, meta-schema constants, and OpenAPI component-key helpers.

16 exports Added in v4.0.0 Source

Constants

Represents the $schema meta-schema URI for JSON Schema Draft-04.

When to use

Use when constructing a Draft-04 JSON Schema document and you need a stable value for the root $schema field.

See

Signature

declare const META_SCHEMA_URI_DRAFT_04: "http://json-schema.org/draft-04/schema#"

Represents the $schema meta-schema URI for JSON Schema Draft-07.

When to use

Use when constructing a Draft-07 JSON Schema document and you need a stable value for the root $schema field.

Details

The exported value is the literal string http://json-schema.org/draft-07/schema#.

See

Signature

declare const META_SCHEMA_URI_DRAFT_07: "http://json-schema.org/draft-07/schema#"

Represents the $schema meta-schema URI for JSON Schema Draft 2020-12.

When to use

Use when you need to populate the $schema field while emitting a JSON Schema document that should declare JSON Schema Draft 2020-12.

Details

The exported value is the literal string https://json-schema.org/draft/2020-12/schema.

See

Signature

declare const META_SCHEMA_URI_DRAFT_2020_12: "https://json-schema.org/draft/2020-12/schema"

Decoding

Parses a raw Draft-07 JSON Schema into a Document<"draft-2020-12">.

When to use

Use when you have a raw JSON Schema object that follows Draft-07 conventions and need the canonical Draft-2020-12 document representation.

Details

This converts Draft-07 tuple syntax (items as array plus additionalItems) to Draft-2020-12 form (prefixItems plus items), splits dependencies into dependentRequired and dependentSchemas, converts plain-name $id fragments to $anchor, and extracts root-level definitions into the definitions field. Local JSON Pointer refs are relocated when one of these structural conversions moves its target.

Gotchas

Unknown and custom keywords are copied as opaque values. Their contents are not treated as nested schemas. Draft-07 keywords such as if / then / else and contains are preserved and their subschemas are converted. Siblings of a valid Draft-07 $ref are ignored according to Draft-07 semantics. The conversion throws when a Draft-07 $id fragment cannot be represented as a Draft-2020-12 $anchor, or when an unknown Draft-07 keyword would become an active Draft-2020-12 keyword after copying.

See

Signature

declare function fromSchemaDraft07(js: JsonSchema): Document<"draft-2020-12">

Example

(Parsing a Draft-07 schema)

import { JsonSchema } from "effect"
const raw: JsonSchema.JsonSchema = {
type: "object",
properties: {
tags: {
type: "array",
items: { type: "string" }
}
}
}
const doc = JsonSchema.fromSchemaDraft07(raw)
doc.dialect // => "draft-2020-12"
doc.schema.properties // => { tags: { type: "array", items: { type: "string" } } }

Parses a raw Draft-2020-12 JSON Schema into a Document<"draft-2020-12">.

When to use

Use when you already have a raw JSON Schema object in Draft-2020-12 format.

Details

This separates $defs from the root schema into the definitions field. Unlike fromSchemaDraft07, this performs no keyword rewriting.

See

Signature

declare function fromSchemaDraft2020_12(js: JsonSchema): Document<"draft-2020-12">

Example

(Parsing a Draft-2020-12 schema)

import { JsonSchema } from "effect"
const raw: JsonSchema.JsonSchema = {
type: "number",
minimum: 0,
$defs: { PositiveInt: { type: "integer", minimum: 1 } }
}
const doc = JsonSchema.fromSchemaDraft2020_12(raw)
doc.schema // => { type: "number", minimum: 0 }
doc.definitions // => { PositiveInt: { type: "integer", minimum: 1 } }

Parses a raw OpenAPI 3.0 JSON Schema into a Document<"draft-2020-12">.

When to use

Use when you need to consume raw JSON Schema objects from an OpenAPI 3.0 specification.

Details

This directly converts OpenAPI 3.0 schema objects to Draft-2020-12. It handles nullable, singular example, boolean exclusiveMinimum and exclusiveMaximum, and OpenAPI component refs. Only values in OpenAPI schema positions are traversed as schemas.

Gotchas

OpenAPI 3.0 nullable is applied only when the same Schema Object has an explicit string type; other constraints such as enum are left unchanged. Unknown keywords, vendor extensions, and annotation values are copied opaquely unless their name would become active in Draft 2020-12 and change meaning, in which case conversion throws. Siblings of a valid OpenAPI 3.0 $ref are ignored.

See

Signature

declare function fromSchemaOpenApi3_0(schema: JsonSchema): Document<"draft-2020-12">

Example

(Parsing an OpenAPI 3.0 nullable schema)

import { JsonSchema } from "effect"
const raw: JsonSchema.JsonSchema = {
type: "string",
nullable: true
}
const doc = JsonSchema.fromSchemaOpenApi3_0(raw)
doc.schema.type // => ["string", "null"]

Parses a raw OpenAPI 3.1 JSON Schema into a Document<"draft-2020-12">.

When to use

Use when you need to consume raw JSON Schema objects from an OpenAPI 3.1 specification.

Details

This rewrites #/components/schemas/... refs to #/$defs/..., normalizes the OpenAPI base dialect URI to Draft 2020-12, converts the deprecated singular example field to examples, then delegates to fromSchemaDraft2020_12.

Gotchas

When both example and examples are present, the singular example is prepended to the array. Custom $schema dialect URIs and unknown keywords are copied opaquely. Component references inside a schema resource identified by $id are left unchanged because they are relative to that resource.

See

Signature

declare function fromSchemaOpenApi3_1(js: JsonSchema): Document<"draft-2020-12">

Example

(Parsing an OpenAPI 3.1 schema)

import { JsonSchema } from "effect"
const raw: JsonSchema.JsonSchema = {
type: "object",
properties: {
user: { $ref: "#/components/schemas/User" }
}
}
const doc = JsonSchema.fromSchemaOpenApi3_1(raw)
doc.schema.properties // => { user: { $ref: "#/$defs/User" } }

Encoding

Converts a Document<"draft-2020-12"> to a Document<"draft-04">.

When to use

Use when you need to output a canonical JSON Schema document in Draft-04 format.

Details

This directly rewrites #/$defs/... refs to #/definitions/..., converts tuple syntax, merges canonical dependencies, lowers const to enum, converts numeric exclusive bounds to the Draft-04 boolean form, lowers conditionals and basic contains through boolean applicators, and converts both the root schema and all definitions.

Gotchas

Unknown and custom keywords are copied as opaque values. Newer annotation keywords are preserved as Draft-04 extensions. Known keywords without a Draft-04 equivalent, including propertyNames, non-default contains cardinality, dynamic references, and unevaluated constraints, cause the conversion to throw instead of being dropped. A conditional with both branches also throws when lowering it would duplicate a nested schema identifier. Conversion also throws when an opaque Draft-2020-12 keyword would collide with an active Draft-04 keyword, or when $id and $anchor occur together because Draft-04 cannot preserve both identifiers.

See

Signature

declare function toDocumentDraft04(document: Document<"draft-2020-12">): Document<"draft-04">

Example

(Converting exclusive bounds)

import { JsonSchema } from "effect"
const doc = JsonSchema.fromSchemaDraft2020_12({
type: "number",
exclusiveMinimum: 0
})
JsonSchema.toDocumentDraft04(doc).schema // => { type: "number", minimum: 0, exclusiveMinimum: true }

Converts a Document<"draft-2020-12"> to a Document<"draft-07">.

When to use

Use when you need to output a canonical JSON Schema document in Draft-07 format.

Details

This rewrites #/$defs/... refs to #/definitions/..., converts Draft-2020-12 tuple syntax (prefixItems plus items) to Draft-07 form (items as array plus additionalItems), merges dependentRequired and dependentSchemas into dependencies, and converts both the root schema and all definitions. Local JSON Pointer refs are relocated when structural keywords move.

Gotchas

Unknown and custom keywords are copied as opaque values. Known keywords that Draft-07 cannot represent cause the conversion to throw instead of being dropped. These include dynamic references, unevaluatedProperties, unevaluatedItems, and non-default minContains or maxContains constraints. Conversion also throws when an opaque Draft-2020-12 keyword would collide with an active Draft-07 keyword, or when $id and $anchor occur together because Draft-07 cannot preserve both identifiers.

See

Signature

declare function toDocumentDraft07(document: Document<"draft-2020-12">): Document<"draft-07">

Example

(Converting to Draft-07)

import { JsonSchema } from "effect"
const doc = JsonSchema.fromSchemaDraft2020_12({
type: "array",
prefixItems: [{ type: "string" }, { type: "number" }],
items: { type: "boolean" }
})
const draft07 = JsonSchema.toDocumentDraft07(doc)
draft07.dialect // => "draft-07"
draft07.schema.items // => [{ type: "string" }, { type: "number" }]
draft07.schema.additionalItems // => { type: "boolean" }

Converts a MultiDocument<"draft-2020-12"> to a MultiDocument<"openapi-3.1">.

When to use

Use when you need to emit an OpenAPI 3.1 multi-document from canonical JSON Schema documents.

Details

This rewrites local #/$defs/... refs to #/components/schemas/... and sanitizes definition keys to match the OpenAPI component key pattern (^[a-zA-Z0-9.\-_]+$) by replacing invalid characters with _. Valid keys are preserved. When sanitized keys collide, the converter appends the first available _1, _2, and subsequent suffix, with allocation independent of definition insertion order. All local refs are updated to use the allocated keys, including refs to paths within a definition.

Gotchas

External refs and local refs outside #/$defs are left unchanged. Conversion throws when a custom keyword would become an active OpenAPI keyword and therefore change meaning. References inside schema resources identified by $id are left unchanged. Conversion throws when an identified root schema references the detached shared definitions pool because OpenAPI cannot preserve that fragment reference.

See

Signature

declare function toMultiDocumentOpenApi3_1(multiDocument: MultiDocument<"draft-2020-12">): MultiDocument<"openapi-3.1">

Example

(Converting to OpenAPI 3.1)

import { JsonSchema } from "effect"
const multi: JsonSchema.MultiDocument<"draft-2020-12"> = {
dialect: "draft-2020-12",
schemas: [{ $ref: "#/$defs/User" }],
definitions: {
User: { type: "object", properties: { name: { type: "string" } } }
}
}
const openapi = JsonSchema.toMultiDocumentOpenApi3_1(multi)
openapi.dialect // => "openapi-3.1"
openapi.schemas[0] // => { $ref: "#/components/schemas/User" }

Models

Definitions interface

Added in v4.0.0 Source

A record of named JSON Schema definitions, keyed by definition name.

When to use

Use as the shared lookup table for named JSON Schema nodes that are referenced from JSON Schema documents.

Details

The map is dialect-neutral. Conversion APIs emit it as $defs, definitions, or components.schemas depending on the target format.

See

  • Document for a single root schema with definitions
  • MultiDocument for multiple root schemas sharing definitions

Signature

interface Definitions extends Record<string, JsonSchema> {
[key: string]: JsonSchema;
}

Dialect type

Added in v4.0.0 Source

The set of JSON Schema dialects supported by this module.

When to use

Use as the dialect marker for JsonSchema documents when parsing, converting, or emitting schemas across the supported formats.

Details

Supported values are "draft-04" for JSON Schema Draft-04, "draft-07" for JSON Schema Draft-07, "draft-2020-12" for JSON Schema Draft 2020-12 and the canonical internal form, "openapi-3.1" for OpenAPI 3.1, and "openapi-3.0" for OpenAPI 3.0.

See

  • Document for a single root schema tagged with a dialect
  • MultiDocument for multiple root schemas tagged with a dialect

Signature

type Dialect = "draft-04" | "draft-07" | "draft-2020-12" | "openapi-3.1" | "openapi-3.0"

Document interface

Added in v4.0.0 Source

A structured container for a single JSON Schema and its associated definitions.

When to use

Use when you need to carry a root schema together with its shared definitions, or when converting between dialects with the from* and to* functions.

Details

The schema field holds the root schema without the definitions collection. Root definitions are stored separately in definitions and referenced via #/$defs/<name> for Draft-2020-12, #/definitions/<name> for Draft-04 and Draft-07, and #/components/schemas/<name> for OpenAPI 3.1 and OpenAPI 3.0.

See

Signature

interface Document<D extends Dialect> {
readonly definitions: Definitions;
readonly dialect: D;
readonly schema: JsonSchema;
}

Example

(Inspecting a parsed document)

import { JsonSchema } from "effect"
const raw: JsonSchema.JsonSchema = {
type: "string",
$defs: { Trimmed: { type: "string", minLength: 1 } }
}
const doc = JsonSchema.fromSchemaDraft2020_12(raw)
doc.dialect // => "draft-2020-12"
doc.schema // => { type: "string" }
doc.definitions // => { Trimmed: { type: "string", minLength: 1 } }

JsonSchema interface

Added in v4.0.0 Source

A plain object representing a single JSON Schema node.

When to use

Use to represent an arbitrary JSON Schema object regardless of dialect.

Details

This is an open record type ([x: string]: unknown) so it can hold any JSON Schema keyword. Most functions in this module accept or return this type.

Signature

interface JsonSchema {
[x: string]: unknown;
}

MultiDocument interface

Added in v4.0.0 Source

Like Document, but carries multiple root schemas that share a single definitions pool.

When to use

Use when generating several schemas, such as a request body and a response body, that reference the same set of definitions.

Details

The schemas tuple is non-empty and contains at least one element.

See

Signature

interface MultiDocument<D extends Dialect> {
readonly definitions: Definitions;
readonly dialect: D;
readonly schemas: readonly [JsonSchema, JsonSchema];
}

Type type

Added in v4.0.0 Source

The JSON Schema primitive type names.

When to use

Use to restrict a JSON Schema type keyword to the supported primitive names.

Signature

type Type = "string" | "number" | "boolean" | "array" | "object" | "null" | "integer"