Smelter 🔥⚗️

JSON Schema to Elixir code generator. Extracts pure Elixir types from raw JSON Schema ore.

Generates Ecto.Schema modules with embedded_schema and changeset/2 for validation.

Features

Installation

Add smelter to your list of dependencies in mix.exs:

def deps do
[
{:smelter, "~> 0.1"}
]
end

Usage

Single Schema

# Parse and resolve a schema
{:ok, schema} = Smelter.parse("path/to/schema.json")
# Generate Elixir code
code = Smelter.generate(schema, module: "MyApp.Schemas.User")
# Or do both in one step
{:ok, code} = Smelter.compile("path/to/schema.json", module: "MyApp.Schemas.User")

Batch Generation

Smelter can process entire directories of JSON Schemas, preserving the folder structure:

Smelter.Batch.generate(
schema_dir: "priv/schemas/2026-01-11",
output_dir: "lib/my_app/schemas",
module_prefix: "MyApp.Schemas"
)

This processes all .json files in the directory, including:

Generated Code

Given a JSON Schema like:

{
"title": "User",
"type": "object",
"required": ["name", "email"],
"properties": {
"name": { "type": "string" },
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0 }
}
}

Smelter generates:

defmodule MyApp.Schemas.User do
@moduledoc """
User
"""
use Ecto.Schema
import Ecto.Changeset
@primary_key false
embedded_schema do
field :age, :integer
field :email, :string
field :name, :string
end
def changeset(struct \\ %__MODULE__{}, params) do
struct
|> cast(params, [:age, :email, :name])
|> validate_required([:email, :name])
end
end

Schema Composition

Smelter handles JSON Schema composition keywords:

allOf

Properties from all schemas are merged together:

{
"allOf": [
{ "$ref": "base.json" },
{ "properties": { "extra": { "type": "string" } } }
]
}

oneOf / anyOf

Generates union type modules that delegate to the appropriate variant:

{
"oneOf": [
{ "$ref": "error.json" },
{ "$ref": "warning.json" }
]
}

Generated code uses a discriminator field (commonly type) to route to the correct schema module.

$defs

Entries in $defs are extracted and generated as separate modules. A schema like:

{
"$defs": {
"Address": {
"type": "object",
"properties": { "city": { "type": "string" } }
}
}
}

Generates MyApp.Schemas.ContainerName.Address module.

Real-World Usage: Bazaar

Bazaar uses Smelter to generate UCP schemas:

mix bazaar.gen.schemas priv/ucp_schemas/2026-01-11

This generates all Elixir modules in lib/bazaar/schemas/ from the official UCP JSON Schemas.

License

Apache-2.0