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 stepValidatedJson<Dto>- Extract and validate a DTO without domain conversionErrorResponse- 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"}]
}
}
}