JSON Schema
Because a schema already describes your data completely, Effect can derive a standard JSON Schema document from it. This is how the same schema you use to decode and validate at runtime also drives external tooling: OpenAPI documents, editor autocompletion, form generators, and contract validation in other languages.
import { Schema } from "effect"
const User = Schema.Struct({ name: Schema.String.check(Schema.isMinLength(1)), age: Schema.Number.check(Schema.isGreaterThanOrEqualTo(0)), email: Schema.optionalKey(Schema.String)})
// Produces a JSON Schema document targeting draft 2020-12.const document = Schema.toJsonSchemaDocument(User)
console.log(JSON.stringify(document.schema, null, 2))// {// "type": "object",// "properties": {// "name": { "type": "string", "minLength": 1 },// "age": { "type": "number", "minimum": 0 },// "email": { "type": "string" }// },// "required": ["name", "age"],// "additionalProperties": false// }The document shape
Section titled “The document shape”Schema.toJsonSchemaDocument returns a JsonSchema.Document with three fields:
dialect— the JSON Schema dialect ("draft-2020-12").schema— the generated JSON Schema for your type.definitions— a pool of reusable sub-schemas referenced via$ref(for example, shared or recursive shapes).
Filters that have a JSON Schema equivalent are emitted automatically:
isMinLength becomes minLength, isGreaterThanOrEqualTo becomes minimum,
isPattern becomes pattern, and so on. An exact-optional field
(Schema.optionalKey) is simply omitted from the required array.
Enriching the output with annotations
Section titled “Enriching the output with annotations”Annotations such as title, description, examples, and default flow
straight into the generated schema. Annotate your schema to produce
documentation-quality output.
import { Schema } from "effect"
const Product = Schema.Struct({ sku: Schema.String.annotate({ title: "SKU", description: "Stock keeping unit", examples: ["ABC-123"] }), price: Schema.Number.check(Schema.isGreaterThan(0)).annotate({ description: "Price in USD" })}).annotate({ title: "Product" })
const document = Schema.toJsonSchemaDocument(Product)
console.log(document.schema.title) // "Product"Options
Section titled “Options”toJsonSchemaDocument accepts an options object to tune the output:
additionalProperties—false(default) forbids extra keys,trueallows them, or pass aJsonSchemato constrain them.generateDescriptions— synthesizedescriptiontext for checks from theirexpectedannotation when you have not supplied one.includeAnnotationKey— a predicate selecting which non-standard annotation keys (vendor extensions, editor hints) to emit.
import { Schema } from "effect"
const schema = Schema.String.annotate({ description: "A name", // A custom, non-standard annotation key. markdownDescription: "The **name** field"})
const document = Schema.toJsonSchemaDocument(schema, { // Whitelist exactly the custom keys you want to surface. includeAnnotationKey: (key) => key === "markdownDescription" || key.startsWith("x-")})
console.log(document.schema)// { type: "string", description: "A name", markdownDescription: "The **name** field" }Targeting other dialects
Section titled “Targeting other dialects”The generated document targets draft 2020-12. To emit an older draft, convert it
with the helpers in the JsonSchema module — for example
JsonSchema.toDocumentDraft07 rewrites the document (and its $refs) to
draft-07.
import { Schema, JsonSchema } from "effect"
const User = Schema.Struct({ name: Schema.String })
const draft202012 = Schema.toJsonSchemaDocument(User)const draft07 = JsonSchema.toDocumentDraft07(draft202012)
console.log(draft07.dialect) // "draft-07"For request/response contracts you usually do not call this directly — the
HTTP API layer generates OpenAPI documents from your schemas for
you. toJsonSchemaDocument is the building block underneath.