vld-utoipa 0.2.0

utoipa integration for the vld validation library — auto-generate OpenAPI schemas from vld definitions
Documentation

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

vld-utoipa

Bridge between vld validation library and utoipa OpenAPI documentation.

Define validation rules once with vld and automatically get utoipa::ToSchema implementation — no need to duplicate schema definitions.

Installation

[dependencies]
vld = { version = "0.1", features = ["openapi"] }
vld-utoipa = "0.1"
utoipa = "5"

Quick Start

use vld::prelude::*;
use vld_utoipa::impl_to_schema;

// 1. Define validated struct as usual
vld::schema! {
    #[derive(Debug)]
    pub struct CreateUser {
        pub name: String => vld::string().min(2).max(100),
        pub email: String => vld::string().email(),
        pub age: Option<i64> => vld::number().int().gte(0).optional(),
    }
}

// 2. One line to bridge to utoipa
impl_to_schema!(CreateUser);

// Now CreateUser implements utoipa::ToSchema and can be used in
// #[utoipa::path(post, path = "/users", request_body = CreateUser)]

Using with #[derive(Validate)]

impl_to_schema! also works with #[derive(Validate)] from vld-derive. This lets you use standard Rust struct syntax with serde attributes like #[serde(rename_all = "camelCase")] and still get OpenAPI schema generation.

[dependencies]
vld = { version = "0.1", features = ["derive", "openapi"] }
vld-utoipa = "0.1"
utoipa = "5"
use vld::Validate;
use vld_utoipa::impl_to_schema;

#[derive(Debug, serde::Deserialize, Validate)]
#[serde(rename_all = "camelCase")]
struct UpdateLocationRequest {
    #[vld(vld::string().min(1).max(255))]
    name: String,
    #[vld(vld::string())]
    street_address: String,
    #[vld(vld::number().int().non_negative().min(1).max(9999))]
    street_number: i64,
    #[vld(vld::string().optional())]
    street_number_addition: Option<String>,
    #[vld(vld::boolean())]
    is_active: bool,
}

impl_to_schema!(UpdateLocationRequest);
// OpenAPI schema properties use camelCase:
// "streetAddress", "streetNumber", "streetNumberAddition", "isActive"

Nested Schemas (auto-registration)

When you use vld::nested!(Type), the nested type is automatically registered in utoipa's components/schemas. No need to list it manually in #[openapi(components(schemas(...)))].

use vld::prelude::*;
use vld_utoipa::impl_to_schema;

vld::schema! {
    #[derive(Debug)]
    pub struct Address {
        pub city: String => vld::string().min(1),
        pub zip: String => vld::string().min(5).max(10),
    }
}

impl_to_schema!(Address);

vld::schema! {
    #[derive(Debug)]
    pub struct CreateUser {
        pub name: String => vld::string().min(2),
        pub address: Address => vld::nested!(Address),
    }
}

impl_to_schema!(CreateUser);

// In OpenAPI spec:
// - CreateUser.address → { "$ref": "#/components/schemas/Address" }
// - Address schema is auto-registered in components

Custom Schema Name

impl_to_schema!(CreateUser, "CreateUserRequest");

Converting Arbitrary JSON Schema

use vld_utoipa::json_schema_to_schema;

let json_schema = serde_json::json!({
    "type": "object",
    "required": ["name"],
    "properties": {
        "name": { "type": "string", "minLength": 1 }
    }
});

let utoipa_schema = json_schema_to_schema(&json_schema);

Supported JSON Schema Features

  • Primitive types: string, number, integer, boolean, null
  • Object with properties and required
  • Array with items, minItems, maxItems
  • oneOf, allOf composites
  • enum values
  • String: minLength, maxLength, pattern, format
  • Number: minimum, maximum, exclusiveMinimum, exclusiveMaximum, multipleOf
  • $ref references
  • description, default, example, title

Running the Example

cargo run -p vld-utoipa --example utoipa_basic

License

MIT