Schema to JSON Schema
Schema.toJsonSchemaDocument generates a JSON Schema Draft 2020-12 document for a schema’s canonical JSON representation.
Internally, Effect first derives Schema.toCodecJson(schema) and then describes that codec’s encoded side. The generated JSON Schema therefore matches the values accepted and produced by the canonical JSON codec, including the JSON representations of Effect data types.
Basic Conversion
Example (Generating JSON Schema for a Struct)
import { Schema } from "effect"
const Person = Schema.Struct({ name: Schema.String, age: Schema.Finite,})
const document = Schema.toJsonSchemaDocument(Person)
document.dialect // => "draft-2020-12"document.schema.type // => "object"document.schema.required // => ["name", "age"]document.schema.additionalProperties // => trueThe returned document contains:
dialect: the source dialect, always"draft-2020-12".schema: the root JSON Schema.definitions: definitions referenced through$ref.
JSON Schema generation is best-effort. Semantics that JSON Schema cannot express exactly may be approximated, and opaque declarations without a structural JSON codec produce an unconstrained schema.
Canonical JSON Representations
For codecs, the output describes the encoded JSON shape rather than the decoded Type. Schema.toJsonSchemaDocument uses the same representation as Schema.toCodecJson, so runtime serialization and the generated document agree.
Example (Describing the Encoded Side)
import { Schema } from "effect"
// Type: number, Encoded: stringSchema.toJsonSchemaDocument(Schema.FiniteFromString).schema // => { type: "string" }Other Drafts
Schema.toJsonSchemaDocument always emits Draft 2020-12. Convert the resulting document with the JsonSchema module when another draft is required.
Example (Converting to Draft 07)
import { JsonSchema, Schema } from "effect"
const schema = Schema.Tuple([Schema.String, Schema.Finite])
const draft2020_12 = Schema.toJsonSchemaDocument(schema)const draft07 = JsonSchema.toDocumentDraft07(draft2020_12)
draft07.dialect // => "draft-07"draft07.schema.items // => [{ type: "string" }, { type: "number" }]JsonSchema.toDocumentDraft04 similarly converts a document to Draft 04.
Annotations
The following standard JSON Schema annotations are emitted automatically:
titledescriptiondefaultexamplesreadOnlywriteOnlyformatcontentEncodingcontentMediaTypecontentSchema
Example (Adding Standard Metadata)
import { Schema } from "effect"
const Username = Schema.String.annotate({ title: "Username", description: "A user name", default: "anonymous", examples: ["alice", "bob"],})
const document = Schema.toJsonSchemaDocument(Username)
document.schema.title // => "Username"document.schema.description // => "A user name"document.schema.default // => "anonymous"document.schema.examples // => ["alice", "bob"]Annotating a Codec’s Encoded Side
Calling .annotate(...) on a codec annotates its decoded side. Use Schema.annotateEncoded for metadata that belongs to the JSON representation.
Example (Annotating Encoded Input)
import { Schema } from "effect"
const schema = Schema.Trim.pipe( Schema.annotateEncoded({ title: "Encoded text", description: "Text before trimming", }),)
const document = Schema.toJsonSchemaDocument(schema)
document.schema.type // => "string"document.schema.title // => "Encoded text"document.schema.description // => "Text before trimming"Custom Annotation Keys
Use includeAnnotationKey to whitelist non-standard annotations such as editor metadata or vendor extensions. Standard keys are always included.
Example (Including Custom Metadata)
import { Schema } from "effect"
const schema = Schema.String.annotate({ description: "A name", markdownDescription: "The **name** field", "x-widget": "text",})
const document = Schema.toJsonSchemaDocument(schema, { includeAnnotationKey: (key) => key === "markdownDescription" || key.startsWith("x-"),})
document.schema.description // => "A name"document.schema.markdownDescription // => "The **name** field"document.schema["x-widget"] // => "text"Filters and Constraints
Built-in filters contribute JSON Schema constraints such as minLength, maximum, pattern, and uniqueItems.
Example (Generating Validation Constraints)
import { Schema } from "effect"
const Username = Schema.String.check( Schema.isMinLength(3), Schema.isMaxLength(20), Schema.isPattern(/^[a-z0-9_]+$/u),)
Schema.toJsonSchemaDocument(Username).schema // => { type: "string", minLength: 2, maxLength: 20, pattern: "^[a-z0-9_]+$" }JavaScript counts UTF-16 code units while JSON Schema counts Unicode code
points. For this reason, isMinLength(3) is represented conservatively as
minLength: 2 so the generated schema does not reject a string accepted by
Effect.
For a custom filter, provide a toJsonSchema callback when its constraint has a JSON Schema equivalent.
Example (Describing a Custom Filter)
import { Schema } from "effect"
const LongString = Schema.String.check( Schema.makeFilter((value) => value.length >= 3, { expected: "a string with at least three characters", toJsonSchema: () => ({ minLength: 3 }), }),)
Schema.toJsonSchemaDocument(LongString).schema // => { type: "string", minLength: 3 }Set generateDescriptions: true to turn a check’s expected annotation into a description when no description was supplied explicitly.
Optional Properties
An optionalKey property is omitted from required. A property defined with optional is also omitted from required, and its explicit undefined case is represented as null because JSON has no undefined value.
Example (Optional Properties)
import { Schema } from "effect"
const schema = Schema.Struct({ name: Schema.optionalKey(Schema.String), nickname: Schema.optional(Schema.String),})
const document = Schema.toJsonSchemaDocument(schema)
console.log(document.schema)/*Output:{ type: "object", properties: { name: { type: "string" }, nickname: { anyOf: [{ type: "string" }, { type: "null" }] } }, additionalProperties: true}*/References and Recursion
An identifier annotation creates a definition and replaces uses of that schema with $ref.
Example (Creating Reusable Definitions)
import { Schema } from "effect"
const Name = Schema.String.annotate({ identifier: "Name" })const Person = Schema.Struct({ name: Name })
const document = Schema.toJsonSchemaDocument(Person)
console.log(document.schema)/*Output:{ type: "object", properties: { name: { $ref: "#/$defs/Name" } }, required: ["name"], additionalProperties: true}*/Recursive schemas require an identifier so their self-references can be emitted as $ref.
Example (Generating a Recursive JSON Schema)
import { Schema } from "effect"
interface Category { readonly name: string readonly categories: ReadonlyArray<Category>}
const Category = Schema.Struct({ name: Schema.String, categories: Schema.Array( Schema.suspend((): Schema.Codec<Category> => Category), ),}).annotate({ identifier: "Category" })
const document = Schema.toJsonSchemaDocument(Category)
console.log(document)/*Output:{ dialect: "draft-2020-12", schema: { $ref: "#/$defs/Category" }, definitions: { Category: { type: "object", properties: { name: { type: "string" }, categories: { type: "array", items: { $ref: "#/$defs/Category" } } }, required: ["name", "categories"], additionalProperties: true } }}*/Generation Options
Schema.toJsonSchemaDocument accepts four options:
referencePolicy: controls which schemas become named references.onExcessProperty:"ignore"by default, leaving unmodeled properties open;"error"closes them when their key space can be represented.generateDescriptions: generate missing check descriptions fromexpectedannotations.includeAnnotationKey: include selected non-standard annotation keys.
Example (Rejecting Additional Properties)
import { Schema } from "effect"
const schema = Schema.Struct({ name: Schema.String })
const document = Schema.toJsonSchemaDocument(schema, { onExcessProperty: "error",})
document.schema.additionalProperties // => falseJSON Strings
Schema.fromJsonString accepts a JSON string and decodes its parsed contents with another schema. Its JSON Schema describes the outer string and marks its media type as JSON.
Example (Describing a JSON String)
import { Schema } from "effect"
const schema = Schema.fromJsonString(Schema.Struct({ name: Schema.String }))
const document = Schema.toJsonSchemaDocument(schema)
document.schema.type // => "string"document.schema.contentMediaType // => "application/json"