axum-error-handler 0.2.0

A simple error handler for axum
Documentation

Axum Error Handler

A simple parser that implemented Axum IntoResponse trait.

Please notice that this is a experimental project. This proc-macros depends on axum, thiserror and serde_json crates.

Basic Usage

use axum_error_handler::AxumErrorResponse;
use thiserror::Error;

#[derive(Debug, Error, AxumErrorResponse)]
pub enum TestError {
    #[error("Bad request: {0}")]
    #[status_code("400")]
    #[code("BAD_REQUEST")]
    BadRequest(String),
    
    #[error("Internal server error {0}")]
    #[status_code("500")]
    #[code("INTERNAL_SERVER_ERROR")]
    InternalError(String),
}

Basic Output

{
  "result": null,
  "error": {
    "code": "BAD_REQUEST",
    "message": "Bad request: invalid input"
  }
}

Nested Response Support

The library supports nested error handling through the #[response(nested)] attribute. This allows inner errors that also implement AxumErrorResponse to be properly forwarded with their own status codes and error details.

Example

use axum_error_handler::AxumErrorResponse;
use thiserror::Error;

#[derive(Debug, Error, AxumErrorResponse)]
pub enum AppError {
    #[error("Bad request: {0}")]
    #[status_code("400")]
    #[code("BAD_REQUEST")]
    BadRequest(String),
    
    // Use nested response to delegate to inner error's response
    #[error("{0}")]
    #[response(nested)]
    ServiceError(#[from] ServiceError),
}

#[derive(Debug, Error, AxumErrorResponse)]
pub enum ServiceError {
    #[error("Authentication error: {0}")]
    #[status_code("401")]
    #[code("AUTHENTICATION_ERROR")]
    AuthenticationError(String),
    
    #[error("Database connection failed: {0}")]
    #[status_code("503")]
    #[code("DATABASE_ERROR")]
    DatabaseError(String),
    
    // Supports multiple levels of nesting
    #[error("{0}")]
    #[response(nested)]
    ValidationError(#[from] ValidationError),
}

#[derive(Debug, Error, AxumErrorResponse)]
pub enum ValidationError {
    #[error("Validation failed: {0}")]
    #[status_code("422")]
    #[code("VALIDATION_ERROR")]
    FieldValidation(String),
    
    #[error("Permission denied: {0}")]
    #[status_code("403")]
    #[code("PERMISSION_DENIED")]
    PermissionDenied(String),
}

Usage

// Direct error
let err = AppError::BadRequest("missing field".to_string());

// Nested error - preserves inner error's status code and details
let service_err = ServiceError::AuthenticationError("invalid token".to_string());
let app_err = AppError::ServiceError(service_err);

// Multi-level nesting
let validation_err = ValidationError::FieldValidation("email format invalid".to_string());
let service_err = ServiceError::ValidationError(validation_err);
let app_err = AppError::ServiceError(service_err);

Nested Response Output

When using nested responses, the inner error's status code, error code, and message are preserved:

{
  "result": null,
  "error": {
    "code": "AUTHENTICATION_ERROR",
    "message": "Authentication error: invalid token"
  }
}

Key Features

  • Status Code Preservation: Nested errors maintain their original HTTP status codes
  • Error Code Forwarding: Custom error codes from inner errors are preserved
  • Multiple Nesting Levels: Supports arbitrarily deep error nesting
  • Automatic Conversion: Use #[from] attribute for automatic error conversion
  • Consistent JSON Format: All responses follow the same {"result": null, "error": {...}} structure