typestar

An error type that responds in Rust

Implement IntoResponse for your error and handlers can use the ? operator.

use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::{routing::get, Json, Router};
use serde_json::json;

enum ApiError {
    NotFound(String),
    Invalid { field: &'static str },
}

impl IntoResponse for ApiError {
    fn into_response(self) -> Response {
        let (status, message) = match self {
            ApiError::NotFound(what) => {
                (StatusCode::NOT_FOUND, format!("no such {what}"))
            }
            ApiError::Invalid { field } => (
                StatusCode::UNPROCESSABLE_ENTITY,
                format!("{field} is invalid"),
            ),
        };
        (status, Json(json!({"error": message}))).into_response()
    }
}

async fn tour() -> Result<String, ApiError> {
    Err(ApiError::NotFound("tour".to_string()))
}

#[tokio::main]
async fn main() {
    let app = Router::new().route("/tours/none", get(tour));
    let listener = tokio::net::TcpListener::bind("127.0.0.1:8080")
        .await
        .unwrap();
    axum::serve(listener, app).await.unwrap();
}

How it works

  1. Each variant maps to a status code and a message.
  2. IntoResponse is what makes it returnable.
  3. Handlers then read like ordinary fallible functions.

Keywords and builtins used here

The run, in numbers

Lines
37
Characters to type
872
Tokens
247
Three-star pace
110 tpm

At the three-star pace of 110 tokens a minute, this run takes about 135 seconds.

Type this snippet

Step 2 of 3 in Responses & middleware, step 7 of 9 in Web services with axum.

← Previous Next →