Peri is a data description library for Elixir, in the spirit of Clojure's
Plumatic Schema and Metosin's Malli. A schema is plain Elixir data: atoms
like :string, literals like {:literal, 42}, tuples, maps, keyword lists,
composed however the data demands. There is no separate DSL to learn; the
schema language is Elixir itself. Peri is data, Peri is Elixir.
Because schemas are data, they are programmable: the same definition can validate structs, coerce string params at the boundary, generate test data, export JSON Schema, build Ecto changesets, or render Phoenix forms.
Installation
defp deps do
[
{:peri, "~> 0.11.1"} # x-release-please-version
]
endQuick Start
defmodule MyApp.Schemas do
import Peri
defschema :user, %{
name: {:required, :string},
age: {:integer, gte: 18},
role: {:enum, [:admin, :user, :guest]}
}
defschema :search, %{
page: {:coerce, :integer},
tags: {:coerce, {:list, :string}, split: ","}
}
end
MyApp.Schemas.user(%{name: "John", age: 25, role: :user})
# => {:ok, %{name: "John", age: 25, role: :user}}
# Boundary data arrives as strings; {:coerce, ...} types it.
MyApp.Schemas.search(%{"page" => "2", "tags" => "elixir,otp"})
# => {:ok, %{page: 2, tags: ["elixir", "otp"]}}Features
- Data as schema: type expressions are plain terms (
:string,{:list, t},{:enum, [...]},{:literal, v}, tuples, maps) nested however the data demands - Boundary codecs:
{:coerce, ...}withPeri.decode/3andPeri.encode/3turns string params into typed data and back; targets include constrained scalars, enums, literals, andsplit:-separated lists - Validation modes: strict by default, permissive when extra keys are fine
- Errors: per-field paths, custom
error:overrides, i18n viaPeri.Error.traverse_errors/2, Ecto-style rendering viaPeri.Error.humanize/1 - Schema algebra: compose with
Peri.merge/2,select/2,except/2; rewrite withPeri.walk/2; reuse with{:ref, ...} - Integrations: Ecto changesets and custom types, Phoenix forms via
Peri.Phoenix.to_form/3, JSON Schema Draft 7 in both directions - Data generation: StreamData-backed
Peri.generate/1with per-fieldgen:overrides - Metadata:
{:meta, type, opts}attaches docs and tooling hints without affecting validation
Documentation
- Types Reference: all types, constraints, coercion, and schema transformation
- Validation Patterns: modes, conditional and dependent validation, decoding/encoding, error handling
- Ecto Integration: changesets and custom Ecto types
- Phoenix Integration: forms and params without Ecto
- Data Generation: sample data and property testing with StreamData
- JSON Schema: Draft 7 export and import
- Refs: recursive and cross-module schema references
Why the Name "Peri"?
From the Greek "περί", meaning "around" or "about": the library wraps data structures with descriptions of what they must conform to.