sift
Schema validation for Gleam — constraints, error accumulation, and field paths.
All errors are collected in a single pass using the use pattern. No short-circuiting, no missing fields.
gleam add sift@1
Quick example
import sift
import sift/string as s
import sift/int as i
pub type UserInput {
UserInput(name: String, email: String, age: Int)
}
pub type User {
User(name: String, email: String, age: Int)
}
pub fn validate(input: UserInput) -> Result(User, List(sift.FieldError)) {
use name <- sift.check("name", input.name, s.min_length(1, "required"))
use email <- sift.check("email", input.email, s.email("invalid email"))
use age <- sift.check("age", input.age, i.between(0, 150, "out of range"))
sift.ok(User(name:, email:, age:))
|> sift.validate
}
Invalid input returns all errors at once:
validate(UserInput(name: "", email: "nope", age: -1))
// -> Error([
// FieldError(path: ["name"], message: "required"),
// FieldError(path: ["email"], message: "invalid email"),
// FieldError(path: ["age"], message: "out of range"),
// ])
Features
- Error accumulation — every field is checked, every error is returned
- Field paths — nested structs and lists produce paths like
["address", "zip"]or["tags", "0"] - Composable —
sift.and,sift.or,sift.notto combine validators - Nested structs —
sift.nestedwith automatic path prefixing - List validation —
sift.eachwith indexed paths - Optional fields —
sift/option.requiredandsift/option.optional - Built-in validators — strings, ints, floats, lists, options
Modules
| Module | Validators |
|---|---|
sift | check, nested, each, ok, validate, and, or, not, equals, custom |
sift/string | min_length, max_length, length, non_empty, matches, one_of, starts_with, ends_with, contains, email, url, uuid |
sift/int | min, max, between, positive, non_negative, one_of |
sift/float | min, max, between, positive |
sift/list | min_length, max_length, non_empty |
sift/option | required, optional |
Further documentation can be found at https://hexdocs.pm/sift.