vld-salvo 0.3.0

Salvo integration for the vld validation library
Documentation

Crates.io docs.rs License Platform GitHub issues GitHub stars

vld-salvo

Salvo integration for the vld validation library.

Features

All extractors implement Salvo's Extractible trait and can be used directly as #[handler] parameters — just like Salvo's built-in JsonBody or PathParam:

Extractor Source Description
VldJson<T> JSON body Validates JSON request body
VldQuery<T> Query string Validates URL query parameters
VldForm<T> Form body Validates URL-encoded form body
VldPath<T> Path params Validates path parameters
VldHeaders<T> HTTP headers Validates request headers
VldCookie<T> Cookie header Validates cookie values

All extractors implement Deref<Target = T> so you access fields directly (e.g. body.name instead of body.0.name).

VldSalvoError implements Salvo's Writer trait — validation failures render as 422 Unprocessable Entity with a JSON error body.

Installation

[dependencies]
vld-salvo = "0.1"
vld = "0.1"
salvo = "0.89"
serde_json = "1"

Quick Start

use salvo::prelude::*;
use vld_salvo::prelude::*;

vld::schema! {
    #[derive(Debug, Clone, serde::Serialize)]
    pub struct CreateUser {
        pub name: String  => vld::string().min(2).max(50),
        pub email: String => vld::string().email(),
    }
}

// VldJson<T> is used directly as a handler parameter!
#[handler]
async fn create(body: VldJson<CreateUser>, res: &mut Response) {
    // Deref lets you access fields directly
    res.render(Json(serde_json::json!({"name": body.name})));
}

#[tokio::main]
async fn main() {
    let router = Router::with_path("users").post(create);
    let acceptor = TcpListener::new("0.0.0.0:5800").bind().await;
    Server::new(acceptor).serve(router).await;
}

Path Parameters

Salvo uses {name} syntax for path parameters:

vld::schema! {
    #[derive(Debug, Clone)]
    pub struct UserId {
        pub id: i64 => vld::number().int().min(1),
    }
}

#[handler]
async fn get_user(p: VldPath<UserId>, res: &mut Response) {
    res.render(Json(serde_json::json!({"id": p.id})));
}

// Router::with_path("users/{id}").get(get_user)

Multiple Extractors

Combine several extractors in a single handler:

#[handler]
async fn handler(
    path: VldPath<UserId>,
    query: VldQuery<Pagination>,
    headers: VldHeaders<AuthHeaders>,
    body: VldJson<UpdateUser>,
    res: &mut Response,
) {
    // path.id, query.page, headers.authorization, body.name
}

Error Response Format

{
  "error": "Validation failed",
  "issues": [
    { "path": ".name", "message": "String must be at least 2 characters" }
  ]
}

Running Examples

cargo run -p vld-salvo --example salvo_basic

Example Requests

# Create user (valid)
curl -X POST http://localhost:5800/users \
  -H 'Content-Type: application/json' \
  -d '{"name":"Alice","email":"alice@example.com","age":30}'

# Create user (invalid — triggers 422)
curl -X POST http://localhost:5800/users \
  -H 'Content-Type: application/json' \
  -d '{"name":"A","email":"bad"}'

# Get user by id
curl http://localhost:5800/users/42

# Search (query params)
curl "http://localhost:5800/search?q=hello&page=1&limit=10"

# Health check
curl http://localhost:5800/health

License

MIT