Parameter validation library for Elixir with Phoenix integration.
Web params arrive as strings in a map with string keys, and every handler ends up repeating the same parsing and bounds checks. Filtr takes a schema, casts each value to the type you declared, runs your validators, and hands back a map with atom keys. In Phoenix, you declare that schema with a param macro that reads like attr from Phoenix Components.
Features
- Phoenix integration - Controller and LiveView support
- Plugin system - extend with your own types and validators
- Three error modes - fallback, strict and raise, overridable per field
- Zero dependencies - Phoenix and LiveView are optional
parammacro - declarative syntax, familiar from Phoenix Components- Nested schemas - deep nesting and lists of nested schemas
Requirements
- Elixir ~> 1.16
- Phoenix >= 1.6.0 (optional, for controller integration)
- Phoenix LiveView >= 0.20.0 (optional, for LiveView integration)
Installation
Add filtr to your dependencies in mix.exs:
def deps do
[
{:filtr, "~> 1.0"}
]
end
To keep the param macro formatted the way the macro reads, add :filtr to your
.formatter.exs:
[import_deps: [:filtr]]
Quick start
Phoenix controller
Params are declared above the action they belong to, and the action receives them already cast and validated.
defmodule MyAppWeb.UserController do
use MyAppWeb, :controller
use Filtr.Controller, error_mode: :raise
param :name, :string, required: true
param :age, :integer, min: 18, max: 120
param :email, :string, required: true, pattern: ~r/@/
def create(conn, params) do
json(conn, %{message: "User #{params.name} created"})
end
param :filters do
param :q, :string, default: ""
param :page, :integer, default: 1, min: 1
end
def search(conn, params) do
json(conn, %{query: params.filters.q, page: params.filters.page})
end
end
See Filtr.Controller for nested params, per-field error modes and custom error handlers.
Phoenix LiveView
URL params are declared once for the whole LiveView, and the validated result is assigned
as :filtr on mount and on every navigation.
defmodule MyAppWeb.SearchLive do
use MyAppWeb, :live_view
use Filtr.LiveView, error_mode: :fallback
param :query, :string, default: ""
param :limit, :integer, default: 10, min: 1, max: 100
def mount(_params, _session, socket) do
# socket.assigns.filtr.query and socket.assigns.filtr.limit
{:ok, socket}
end
end
See Filtr.LiveView for the hooks it attaches and the modes it accepts.
Standalone
Outside Phoenix, write the schema as a map and call Filtr.run/3.
schema = %{
name: %{type: :string, required: true, validators: [min: 2]},
age: %{type: :integer, validators: [min: 18, max: 120]},
tags: %{type: {:list, :string}, default: [], validators: [max: 5]}
}
Filtr.run(schema, %{"name" => "John Doe", "age" => "25"})
# %{name: "John Doe", age: 25, tags: [], _valid?: true}
Error modes
Every run happens in one of three modes. :fallback is the default, and replaces a bad
value with its default or nil. :strict leaves {:error, [message]} tuples in the
result for you to inspect, usually with Filtr.collect_errors/1. :raise raises on the
first bad value.
config :filtr, error_mode: :strict
The result map always carries a _valid? boolean, in every mode, so you never have to
scan the fields to know whether the run was clean.
Where to read next
The reference documentation lives with the code:
Filtr- schema fields, error modes, nested schemas, custom cast and validator functionsFiltr.Controller- theparammacro in controllers, and custom error handlersFiltr.LiveView- params in LiveViews and how they reach the assignsFiltr.DefaultPlugin- the built-in types and every validator they supportFiltr.Plugin- writing, registering and overriding plugins
Distributed under the MIT License.