Sinter logo

Sinter

Unified schema definition, validation, and JSON Schema for Elixir

CI

Sinter is a runtime-first schema library for JSON-shaped data. Schemas are defined once and used for validation, coercion, and JSON Schema generation. By default, schema fields are string-keyed to avoid atom leaks and to match JSON wire formats.

Highlights

  • String-keyed schema fields by default (safe for untrusted input)
  • Nested object schemas with Schema.object/1 and {:object, ...}
  • Dual JSON Schema drafts (2020-12 default, Draft 7 for providers)
  • JSON encode/decode helpers with aliasing and omit/nil handling
  • JSV-backed JSON Schema validation

Installation

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

def deps do
  [
    {:sinter, "~> 0.1.0"}
  ]
end

Quick Start

schema = Sinter.Schema.define([
  {:name, :string, [required: true, min_length: 2]},
  {:age, :integer, [optional: true, gteq: 0]},
  {:profile,
   {:object,
    [
      {:nickname, :string, [optional: true]},
      {:joined_at, :datetime, [optional: true]}
    ]}, [optional: true]}
], strict: true)

{:ok, validated} =
  Sinter.Validator.validate(schema, %{
    "name" => "Ada",
    "age" => "36",
    "profile" => %{"joined_at" => "2024-01-01T12:00:00Z"}
  }, coerce: true)

validated["name"]
# => "Ada"

Schema Definition

Sinter accepts atom or string field names but stores them internally as strings.

# Runtime schema definition
schema = Sinter.Schema.define([
  {:title, :string, [required: true]},
  {:tags, {:array, :string}, [optional: true, min_items: 1]}
])

# Compile-time schema definition (same engine under the hood)
defmodule PostSchema do
  use Sinter.Schema

  use_schema do
    option :title, "Post"
    option :strict, true

    field :title, :string, required: true
    field :tags, {:array, :string}, optional: true, min_items: 1
  end
end

Nested Objects

Use Schema.object/1 (or {:object, field_specs}) to model structured data.

address = Sinter.Schema.object([
  {:street, :string, [required: true]},
  {:zip, :string, [required: true]}
])

schema = Sinter.Schema.define([
  {:name, :string, [required: true]},
  {:address, address, [required: true]}
])

Validation

{:ok, data} = Sinter.Validator.validate(schema, %{
  "name" => "Ada",
  "address" => %{"street" => "Main", "zip" => "12345"}
})

JSON Encode/Decode Helpers

Sinter.JSON combines the transform pipeline with JSON encoding/decoding.

payload = %{
  name: "Ada",
  profile: %{
    nickname: Sinter.NotGiven.omit(),
    joined_at: ~N[2024-01-01 12:00:00]
  }
}

{:ok, json} = Sinter.JSON.encode(payload, formats: %{joined_at: :iso8601})
{:ok, decoded} = Sinter.JSON.decode(json, schema, coerce: true)

# Aliases are applied for outbound payloads
{:ok, json} =
  Sinter.JSON.encode(payload,
    aliases: %{name: "full_name"},
    formats: %{joined_at: :iso8601}
  )

JSON Schema Generation

json_schema = Sinter.JsonSchema.generate(schema)
# Draft 2020-12 by default

openai_schema = Sinter.JsonSchema.for_provider(schema, :openai)
# Draft 7 + recursive strictness for provider expectations

json_schema = Sinter.JsonSchema.generate(schema, draft: :draft7)
:ok = Sinter.JsonSchema.validate_schema(json_schema)

Convenience Helpers

{:ok, 42} = Sinter.validate_type(:integer, "42", coerce: true)
{:ok, "user@example.com"} =
  Sinter.validate_value(:email, :string, "user@example.com", format: ~r/@/)

{:ok, values} =
  Sinter.validate_many([
    {:string, "hello"},
    {:integer, 42},
    {:email, :string, "test@example.com", [format: ~r/@/]}
  ])

Dynamic Schema Creation

examples = [
  %{"name" => "Alice", "age" => 30},
  %{"name" => "Bob", "age" => 25}
]

schema = Sinter.infer_schema(examples)

input_schema = Sinter.Schema.define([{:query, :string, [required: true]}])
output_schema = Sinter.Schema.define([{:answer, :string, [required: true]}])
program_schema = Sinter.merge_schemas([input_schema, output_schema])

Examples

Run everything at once:

examples/run_all.sh

Or run individual scripts from examples/:

  • basic_usage.exs
  • readme_comprehensive.exs
  • json_schema_generation.exs
  • advanced_validation.exs
  • dspy_integration.exs

License

MIT