
handle errors properly in Axum
How to Handle Errors Properly in Axum (Without Making a Mess)
Comments (3)
thank you everyone, for reading this blog.
nice to know about axum error handing
nice dost

How to Handle Errors Properly in Axum (Without Making a Mess)
thank you everyone, for reading this blog.
nice to know about axum error handing
nice dost
When you first build an API in Rust with Axum, error handling feels simple:
async fn handler() -> Result<Json<User>, StatusCode> {}
But as your app grows, this quickly becomes messy:
So how do you structure this cleanly?
Let's walk through a real-world pattern.
Imagine a simple endpoint
POST /users
It:
But:
That's the challenge.
Instead of returning random status codes, we define one central error type:
enum AppError {
JsonRejection(JsonRejection),
TimeError(time_library::Error),
}
Now our whole application speaks one error language.
This is important.
You never want 20 different error types leaking into handlers.
? Work AutomaticallyInside the handler:
let created_at = Timestamp::now()?;
But wait...
Timestamp::now()` returns:
Result<Timestamp, time_library::Error>
Our handler returns:
Result<_, AppError>
How does ? know what to do ?
Because we implemented
impl From<time_library::Error> for AppError
This is the magic
When ? sees an error, it automatically converts it using From.
Under the hood, this:
Timestamp::now()?;
Becomes:
match Timestamp::now() {
Ok(val) => val,
Err(e) => return Err(AppError::from(e)),
}
This is clean, powerful and idiomatic Rust.
Now comes the most important pieces:
impl IntoResponse for AppError
This tells Axum: "If a handler returns
Err(AppError)
```, here's how to turn it into an HTTP response."
Inside, we seperate two world:
1. Client Errors (Bad JSON)
````rust
AppError::JsonRejection(rejection)
We:
Because the client messed up
AppError::TimeError(_)
We:
Never leak internal errors to users.
That's production mindset.
Axum’s default Json<T> returns plain text on error.
We want consistent JSON errors.
So we wrapt it:
#[derive(FromRequest)]
#[from_request(via(axum::Json), rejection(AppError))]
struct AppJson<T>(T);
Now JSON errors automatically becomes AppError
This keeps formatting consistent across the app.
Let's say the time library fails:
Here's what happens:
rust Timestamp::now()? converts error -> rust AppErrorrust Err(AppError)rust into_response()Client sees:
{
"message": "Something went wrong"
}
Server logs:
error from time_library err=failed to get time
That's exactly what we want.
This pattern gives you:
It scales beautifully.
The real hero in this entire design is:
impl From<SomeError> for AppError
That single implementation unlocks:
? usageRust's error system isn't just strict.
It's composable.
Beginner Rust code looks like this:
if let Err(e) = something() {
return Err(StatusCode::INTERNAL_SERVER_ERROR);
}
even i wrote this type of code, now I am going to understand and write clean code.
Professional Rust code looksl ike this:
rust something()?
With:
rust From implementationsThat's the difference between "it works" and "it's designed".
Thank you for reading this blog.
References: Axum Anyhow Error Example