Basic Error Handling
1. Welcome and Icebreaker:
Let’s kick things off with a fun question: What’s the most confusing or funny error message you’ve ever seen in programming? Share your story!
2. Outline the Session:
Here’s what we’ll cover today:
- Concept Explanation: How Axum handles errors, why error handling matters, and how to customize error responses for better APIs.
- Live Coding Demonstration: We’ll implement custom error types, return structured error responses, and see how to use them in real endpoints.
- Tasks: You’ll get hands-on practice by handling errors in your own endpoints.
Learning Outcomes:
- Understand Axum’s default error handling and its limitations.
- Implement custom error types using enums and return structured error responses (e.g., JSON).
- Learn best practices for error handling in web APIs.
Expectations: Ask questions, try creating your own error types, and experiment with error responses. Participation is encouraged!
3. Concept Explanation:
Error handling is crucial for building robust APIs. By default, Axum returns generic error responses, which may not be helpful for clients. To improve this, you can define your own error types and implement the IntoResponse trait for them. This allows you to:
- Return custom status codes (e.g., 400, 404, 500).
- Send structured error messages (like JSON objects) to clients.
- Make your API more predictable and easier to debug.
How it works:
- Define an enum for your error types (e.g.,
MyError). - Implement
IntoResponsefor your error enum. This lets you control how errors are converted into HTTP responses. - In your handlers, return
Result<T, MyError>. If an error occurs, Axum will use yourIntoResponseimplementation to generate the response.
Why use enums for errors?
- Enums let you represent different error cases clearly (e.g.,
NotFound,BadRequest,InternalError). - You can attach data or messages to each variant if needed.
Best Practices:
- Always provide meaningful error messages.
- Use appropriate HTTP status codes.
- Avoid leaking sensitive information in error responses.
Common Pitfalls:
- Forgetting to implement
IntoResponsefor your error type (your handlers won’t compile). - Returning uninformative or overly generic error messages.
- Not covering all error cases in your enum.
4. Live Coding Demonstration:
Let’s see how to implement custom error handling in Axum:
use axum::{response::{IntoResponse, Response}, http::StatusCode, Json};
use serde::Serialize;
#[derive(Serialize)]
struct ErrorResponse {
error: String,
}
enum MyError {
NotFound,
BadRequest,
InternalError, // Log internal details on the server, not in the response
}
impl IntoResponse for MyError {
fn into_response(self) -> Response {
let (status, error_msg) = match self {
MyError::NotFound => (StatusCode::NOT_FOUND, "Not found".to_string()),
MyError::BadRequest => (StatusCode::BAD_REQUEST, "Bad request".to_string()),
MyError::InternalError => (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error".to_string()),
};
let body = Json(ErrorResponse { error: error_msg });
(status, body).into_response()
}
}
// Example handler that returns different errors
async fn handler(id: Result<axum::extract::Query<IdQuery>, axum::extract::rejection::QueryRejection>) -> Result<String, MyError> {
let id = id.map_err(|_| MyError::BadRequest)?.0.id;
match id {
1 => Ok("Found item 1".to_string()),
_ => Err(MyError::NotFound),
}
}
#[derive(serde::Deserialize)]
struct IdQuery { id: u32 }
// Example handler for internal error
async fn internal_error_handler() -> Result<String, MyError> {
Err(MyError::InternalError)
}
Explanation:
- We define an
ErrorResponsestruct to serialize error messages as JSON. - The
MyErrorenum represents different error cases. Do not include raw internal error details in a 500 response. - Request extractors reject before a handler runs unless you extract
Result<Extractor, Rejection>and map the error yourself, as shown for?id=1. - The
IntoResponseimplementation maps each error to a status code and message. - Handlers return
Result<T, MyError>, and Axum takes care of converting errors to HTTP responses.
5. Tasks
Try these exercises to practice error handling in Axum:
- Add a New Error Variant:
- Add a
Forbiddenvariant to theMyErrorenum and return it from a handler when a user is not allowed to access a resource.
- Add a
- Custom JSON Error Message:
- Add a safe, generic JSON message for
InternalErrorwhile logging the underlying details server-side.
- Add a safe, generic JSON message for
- Handle Errors in POST/PUT Endpoints:
- Write a POST or PUT handler that validates input and returns a
BadRequesterror if the input is invalid.
- Write a POST or PUT handler that validates input and returns a
- Bonus:
- Try logging errors to the console or a file before returning the response.
Happy coding! If you have questions or want to share your solutions, let me know!