Filtr logo

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

Requirements

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.

The reference documentation lives with the code:

Distributed under the MIT License.