Skip to main content

Crate axum_error_sets

Crate axum_error_sets 

Source
Expand description

Typed, composable HTTP error sets for axum, with OpenAPI generation through aide.

Instead of one large error enum per application, each function lists the exact HTTP status codes it can return, as a tuple in its return type:

async fn get_user() -> ApiResult<String, (Unauthorized, NotFound<String>)> {
    // ...
}

Returning a status code that isn’t in the set is a compile error, and with the aide feature every status code in the set appears in the generated OpenAPI documentation.

§Building blocks

  • Status codes. Every 4xx and 5xx status code has a wrapper type in codes, such as NotFound<T>. The wrapped value T is the response body and must implement IntoResponse. It defaults to (), which means an empty body.
  • Error sets. ApiResponse<S> is an error response whose status code is one of the codes in the tuple S. ApiResult<T, S> is short for Result<T, ApiResponse<S>>.
  • ? conversion. Any status code C converts into ApiResponse<S> when S contains C, so ? works directly. The order of the codes in the tuple doesn’t matter.
  • Wrapping errors. ResultStatusExt adds methods to every Result for giving its error a status code (with_status, into_status), changing it (change_status), or changing the body (map_status, map_status_into).
  • Growing sets. ApiResultExt::into_superset turns a result with a small error set into one with a larger set, so functions with narrow sets can be called from functions with wider ones.

§Example

use axum::Json;
use axum_error_sets::{
    ApiResult, ApiResultExt as _, ResultStatusExt as _,
    codes::{Internal, NotFound, Unauthorized},
};

fn check_token(token: &str) -> Result<(), Unauthorized> {
    if token.is_empty() {
        return Err(Unauthorized(()));
    }
    Ok(())
}

fn find_user(id: u32) -> ApiResult<String, (NotFound<String>,)> {
    let name = lookup(id)
        .ok_or("no such user")
        // `&str` error -> `NotFound<String>`
        .into_status::<NotFound, String>()?;
    Ok(name)
}

async fn get_user(
    token: String,
    id: u32,
) -> ApiResult<Json<String>, (Unauthorized, NotFound<String>, Internal<String>)> {
    // `Unauthorized` is in the set, so `?` converts it.
    check_token(&token)?;

    // `(NotFound<String>,)` is a subset of this handler's set.
    let name = find_user(id).into_superset()?;

    // A `String` error -> `Internal<String>`
    let name = normalize(name).with_status::<Internal>()?;

    Ok(Json(name))
}

Returning a status code that isn’t in the set doesn’t compile:

async fn handler() -> ApiResult<(), (NotFound,)> {
    Err(Forbidden(()))?; // error: `(NotFound,): Contains<Forbidden>` is not satisfied
    Ok(())
}

More examples are in the examples directory.

§Responses

When an ApiResponse is returned, the response has the status code of the wrapper it was created from, even if the body’s own response sets a different status. The body’s IntoResponse runs as soon as the ApiResponse is created, not when the handler returns. Standard axum behaves differently here, which matters if into_response has side effects such as logging.

§OpenAPI with aide

With the aide feature enabled, ApiResponse<S> implements aide::OperationOutput whenever every body type in S does. Each status code in the set is then documented as a response of the operation. This works for sets of up to 16 status codes.

§Typed routing

axum-typed-routing is a companion crate for declaring a route’s path and parameters next to its handler. With its api_route macro, the handler’s error set shows up in the generated OpenAPI documentation automatically.

§Feature flags

Modules§

codes
Wrapper types for every 4xx and 5xx HTTP status code.

Structs§

ApiResponse
An error response whose status code is one of the codes in the set S.

Traits§

ApiResultExt
Methods on Result<T, ApiResponse<S>>.
ResultStatusExt
Methods on any Result for giving its error an HTTP status code, and for changing the status code or body of an error that already has one.
StatusProvider
A wrapper type that pairs a body with a fixed HTTP status code.

Type Aliases§

ApiResult
Short for Result<T, ApiResponse<S>>.