Crate domainstack_axum

Crate domainstack_axum 

Source
Expand description

§domainstack-axum

Axum integration for domainstack validation with automatic DTO→Domain conversion.

This crate provides Axum extractors that automatically deserialize, validate, and convert DTOs to domain types—returning structured error responses on validation failure.

§What it provides

  • DomainJson<T, Dto> - Extract JSON, validate, and convert DTO to domain type in one step
  • ValidatedJson<Dto> - Extract and validate a DTO without domain conversion
  • ErrorResponse - Automatic structured error responses with field-level details

§Example - DomainJson

use axum::{routing::post, Router, Json};
use domainstack::prelude::*;
use domainstack_axum::{DomainJson, ErrorResponse};
use serde::Deserialize;

#[derive(Deserialize)]
struct CreateUserDto {
    name: String,
    age: u8,
}

struct User {
    name: String,
    age: u8,
}

impl TryFrom<CreateUserDto> for User {
    type Error = domainstack::ValidationError;

    fn try_from(dto: CreateUserDto) -> Result<Self, Self::Error> {
        validate("name", dto.name.as_str(), &rules::min_len(2).and(rules::max_len(50)))?;
        validate("age", &dto.age, &rules::range(18, 120))?;
        Ok(Self { name: dto.name, age: dto.age })
    }
}

// Type alias for cleaner handler signatures
type UserJson = DomainJson<User, CreateUserDto>;

async fn create_user(
    UserJson { domain: user, .. }: UserJson
) -> Result<Json<String>, ErrorResponse> {
    // user is guaranteed valid here!
    Ok(Json(format!("Created: {}", user.name)))
}

// In your main.rs or server setup:
fn setup_router() -> Router {
    Router::new().route("/users", post(create_user))
}

§Example - ValidatedJson

use axum::{routing::post, Router, Json};
use domainstack::Validate;
use domainstack_axum::{ValidatedJson, ErrorResponse};

#[derive(Debug, Validate, serde::Deserialize)]
struct UserDto {
    #[validate(length(min = 2, max = 50))]
    name: String,

    #[validate(range(min = 18, max = 120))]
    age: u8,
}

async fn create_user(
    ValidatedJson(dto): ValidatedJson<UserDto>
) -> Result<Json<UserDto>, ErrorResponse> {
    // dto is guaranteed valid here!
    Ok(Json(dto))
}

§Error Response Format

On validation failure, returns a 400 Bad Request with structured errors:

{
  "code": "VALIDATION",
  "status": 400,
  "message": "Validation failed with 2 errors",
  "details": {
    "fields": {
      "name": [{"code": "min_length", "message": "Must be at least 2 characters"}],
      "age": [{"code": "out_of_range", "message": "Must be between 18 and 120"}]
    }
  }
}

Structs§

DomainJson
ErrorResponse
ValidatedJson