A Gleam library for generating JSON Schema documents (Draft 7 compliant). I looked for a simple way to create JSON schemas in Gleam but every things I tried where either outdated or incomplete. This library was born out of that need. This library provides a fluent API for building JSON schemas programmatically, making it easy to create validation schemas for APIs, configuration files, and data structures.
gleam add jscheam
import jscheam/schema import gleam/json // Create simple types let name_schema = schema.string() let age_schema = schema.integer() let active_schema = schema.boolean() let score_schema = schema.float() // Generate JSON Schema let json_schema = schema.to_json(name_schema) |> json.to_string() // Result: {"type":"string"}
import jscheam/schema import gleam/json // Create an object with default additional properties behavior (allows any) let user_schema = schema.object([ schema.prop("name", schema.string()), schema.prop("age", schema.integer()), schema.prop("email", schema.string()) ]) let json_schema = schema.to_json(user_schema) |> json.to_string() // Result: { // "type": "object", // "properties": { // "name": {"type": "string"}, // "age": {"type": "number"}, // "email": {"type": "string"} // }, // "required": ["name", "age", "email"] // Note: additionalProperties is omitted (defaults to true as per JSON Schema Draft 7) // } // Create an object with strict additional properties let strict_user_schema = schema.object([schema.prop("name", schema.string())]) |> schema.disallow_additional_props() |> schema.to_json(strict_user_schema) |> json.to_string() // Result: {..., "additionalProperties": false} // Create an object with constrained additional properties let constrained_user_schema = schema.object([schema.prop("id", schema.string())]) |> schema.constrain_additional_props(schema.string()) |> schema.to_json(constrained_user_schema) |> json.to_string() // Result: {..., "additionalProperties": {"type": "string"}} // Explicitly allow additional properties let explicit_allow_schema = schema.object([ schema.prop("name", schema.string()) ]) |> schema.allow_additional_props() |> schema.to_json(explicit_allow_schema) |> json.to_string() // Result: {..., "additionalProperties": true}
import jscheam/schema import gleam/json let user_schema = schema.object([ schema.prop("name", schema.string()) |> schema.description("User's full name"), schema.prop("age", schema.integer()) |> schema.optional(), schema.prop("email", schema.string()) |> schema.description("User's email address") |> schema.optional() ]) let json_schema = schema.to_json(user_schema) |> json.to_string() // Result: { // "type": "object", // "properties": { // "name": {"type": "string", "description": "User's full name"}, // "age": {"type": "number"}, // "email": {"type": "string", "description": "User's email address"} // }, // "required": ["name"] // }
import jscheam/schema import gleam/json // Array of strings let tags_schema = schema.array(schema.string()) // Array of objects let users_schema = schema.array( schema.object([ schema.prop("name", schema.string()), schema.prop("age", schema.integer()) |> schema.optional() ]) ) let json_schema = schema.to_json(tags_schema) |> json.to_string() // Result: { // "type": "array", // "items": {"type": "string"} // }
Union types allow a property to accept multiple types. Some API require all fields to be "required"" (no optional fields), so the only way to add nullability is to use union types.
import jscheam/schema import gleam/json // Simple union: string or null let nullable_string_schema = schema.union([schema.string(), schema.null()]) // Used in an object let user_schema = schema.object([ schema.prop("name", schema.string()), schema.prop("nickname", schema.union([schema.string(), schema.null()])) |> schema.description("Optional nickname, can be string or null") ]) let json_schema = schema.to_json(user_schema) |> json.to_string() // Result: { // "type": "object", // "properties": { // "name": {"type": "string"}, // "nickname": { // "type": ["string", "null"], // "description": "Optional nickname, can be string or null" // } // }, // "required": ["name", "nickname"] // }
Constraints allow you to add validation rules to your schema properties. jscheam supports enum and pattern constraints with more to come in the future.
Enum constraints restrict values to a fixed set of allowed values.
It uses the json module to define the allowed values as enum values can be any valid JSON type (string, number, boolean, null, ...)
import jscheam/schema import gleam/json // String enum let color_schema = schema.object([ schema.prop("color", schema.string()) |> schema.enum([ json.string("red"), json.string("green"), json.string("blue") ]) |> schema.description("Primary colors only") ]) // Mixed type enum with union let status_schema = schema.object([ schema.prop("status", schema.union([schema.string(), schema.null(), schema.integer()])) |> schema.enum([ json.string("active"), json.string("inactive"), json.null(), json.int(42) ]) |> schema.description("Status with mixed types") ]) let json_schema = schema.to_json(color_schema) |> json.to_string() // Result: { // "type": "object", // "properties": { // "color": { // "type": "string", // "enum": ["red", "green", "blue"], // "description": "Primary colors only" // } // }, // "required": ["color"] // }
Pattern constraints use regular expressions to validate string values:
import jscheam import gleam/json // Email validation let user_schema = schema.object([ schema.prop("email", schema.string()) |> schema.pattern("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$") |> schema.description("Valid email address"), schema.prop("phone", schema.string()) |> schema.pattern("^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$") |> schema.description("Phone number in US format") ]) let json_schema = schema.to_json(user_schema) |> json.to_string() // Result: { // "type": "object", // "properties": { // "email": { // "type": "string", // "pattern": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", // "description": "Valid email address" // }, // "phone": { // "type": "string", // "pattern": "^(\\([0-9]{3}\\))?[0-9]{3}-[0-9]{4}$", // "description": "Phone number in US format" // } // }, // "required": ["email", "phone"] // }
import jscheam import gleam/json let profile_schema = schema.object([ schema.prop("user", schema.object([ schema.prop("name", schema.string()), schema.prop("age", schema.integer()) |> schema.optional() ])), schema.prop("preferences", schema.object([ schema.prop("theme", schema.string()) |> schema.description("UI theme preference"), schema.prop("notifications", schema.boolean()) |> schema.optional() ])), schema.prop("tags", schema.array(schema.string())) |> schema.description("User tags") ]) let json_schema = schema.to_json(profile_schema) |> json.to_string() // Result: { // "type": "object", // "properties": { // "user": { // "type": "object", // "properties": { // "name": {"type": "string"}, // "age": {"type": "number"} // }, // "required": ["name"] // }, // "preferences": { // "type": "object", // "properties": { // "theme": {"type": "string", "description": "UI theme preference"}, // "notifications": {"type": "boolean"} // }, // "required": ["theme"] // }, // "tags": { // "type": "array", // "items": {"type": "string"}, // "description": "User tags" // } // }, // "required": ["user", "preferences"] // }
- Conditional Schema Validation
dependentRequired- conditionally requires that certain properties must be present based on the presence of other propertiesdependentSchemas- conditionally applies different schemas based on the presence of other propertiesif - then - else- conditional schema validation based on the value of a property
- String restrictions:
format(format)- Format validation (email, uri, date-time, etc.)
- Number restrictions:
minimum(n)/maximum(n)- Value range constraints
- Array restrictions:
min_items(n)/max_items(n)- Array length constraints
gleam run # Run the project gleam test # Run the tests
Contributions are welcome! Please feel free to submit issues and enhancement requests.
This project is licensed under the MIT License.