Runtime validation for Rust, the Zod way

vld describes the shape of your data once and checks untrusted input against it: JSON bodies, query strings, config files, database rows. Errors list every problem with its path, and the same schema produces OpenAPI.

What you get

  • Schemas as values
    Chain checks on strings, numbers, dates, collections and objects, then parse JSON or serde values into typed Rust.
  • Typed structs
    schema! and #[derive(Validate)] generate parse(), field-level errors and lenient parsing for your own types.
  • Every error at once
    Typed issue codes with paths, flatten/treeify/prettify helpers, custom messages and bundled translations.
  • OpenAPI from the same code
    JSON Schema and OpenAPI 3.1 documents, nested components and breaking-change diffs.
  • Web extractors
    Axum, Actix, ntex, Poem, Rama, Rocket, Salvo, Tower, Warp and Tonic with one error format and body limits.
  • Beyond HTTP
    Databases, message queues, config files, CLI arguments, UI frameworks, TypeScript codegen and fake data.

Two minutes to the first parse

Add the crate, describe a struct and call parse(). Invalid input returns a VldError with every issue, not just the first.
Cargo.toml
[dependencies]
vld = { version = "0.5", features = ["derive", "openapi"] }
main.rs
use vld::prelude::*;

vld::schema! {
    #[derive(Debug)]
    pub struct User {
        pub name: String => vld::string().min(2).max(50),
        pub email: String => vld::string().email(),
        pub age: Option<i64> => vld::number().int().gte(18).optional(),
    }
}

let user = User::parse(r#"{"name": "Alex", "email": "alex@example.com"}"#)?;
assert_eq!(user.age, None);