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 asNotFound<T>. The wrapped valueTis the response body and must implementIntoResponse. 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 tupleS.ApiResult<T, S>is short forResult<T, ApiResponse<S>>. ?conversion. Any status codeCconverts intoApiResponse<S>whenScontainsC, so?works directly. The order of the codes in the tuple doesn’t matter.- Wrapping errors.
ResultStatusExtadds methods to everyResultfor 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_supersetturns 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
aide: implementsaide::OperationOutputforApiResponse.
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§
- ApiResult
Ext - Methods on
Result<T, ApiResponse<S>>. - Result
Status Ext - Methods on any
Resultfor giving its error an HTTP status code, and for changing the status code or body of an error that already has one. - Status
Provider - A wrapper type that pairs a body with a fixed HTTP status code.
Type Aliases§
- ApiResult
- Short for
Result<T, ApiResponse<S>>.