Expand description
actix_failwrap
/หรฆk.tษชks หfeษชl.rรฆp/ (โaktiks fail-rapโ) A micro-package that enables ergonomic error propagation (via thiserror
) inside Actix Web route handlers.
This crate allows you to:
- โ
Assign HTTP status codes to your
thiserror
enums. - ๐งฉ Customize the HTTP response with a builder function.
- โก Use the
?
operator naturally inside route handlers.
ยงTable of Contents ๐
ยงFeatures ๐
-
โ Automatic error-to-response conversion using
thiserror
enums Define route errors with#[derive(ErrorResponse)]
to auto-generateHttpResponse
. -
๐งฉ Custom response transformation per error enum Use
#[transform_response(fn)]
to modify headers, body, or status codes. -
๐ง Per-variant status code overrides Set status codes using
#[status_code(...)]
โ supports both constants and numbers. -
๐ Fallback behavior for unannotated variants Variants without
#[status_code]
fall back to#[default_status_code]
or HTTP 500. -
โ๏ธ Extractor error mapping with
#[error_override(...)]
Map deserialization or extractor failures to your own enum variant. -
โก Minimal boilerplate route macros with
#[proof_route(...)]
Use?
with error enums directly and skipactix_web
macro imports.
ยงInstallation ๐ฆ
[!IMPORTANT] The
actix_failwrap
macros rely onthiserror
for theDisplay
implementation, and are tightly coupled withactix-web
for building HTTP responses.
This crate is published on crates.io
and is intended for use alongside actix-web
and thiserror
.
Add all three to your Cargo.toml
:
[dependencies]
actix-web = "4"
thiserror = "1"
actix_failwrap = "1.0.0"
ยงUsage Example ๐ค
This example shows a login route in Actix Web using actix_failwrap
.
In your project you may have a module that declares models, in this case a User
model.
In that file you may declare your thiserror
error that you may re-use for your handler.
use serde::{Serialize, Deserialize};
use actix_failwrap::ErrorResponse;
use thiserror::Error;
// Custom error transformation function used by #[transform_response]
// Converts an error into a response with an "Error" header.
fn error_to_header(mut response: HttpResponseBuilder, error: String) -> HttpResponse {
response.insert_header(("Error", error)).finish()
}
// Define a custom error enum for user-related errors.
#[derive(ErrorResponse, Error, Debug)]
// Default fallback for variants without #[status_code],
// if the attribute is not present, the default status code will be 500.
#[default_status_code(InternalServerError)]
// Function used to transform the final HttpResponse,
// if the attribute not present, the Display is mapped to the body.
#[transform_response(error_to_header)]
pub enum UserError {
#[error("Either the email or the password is invalid. Please check the input credentials")]
// This can also be a numeric HTTP status code.
#[status_code(Unauthorized)]
InvalidCredentials,
#[error("Missing credentials, please, introduce your email and password.")]
#[status_code(BadRequest)]
MissingCredentials,
}
#[derive(Serialize, Deserialize)]
pub struct UserCredentials {
pub email: String,
pub password: String,
}
// Simulates a function that attempts to authenticate a user and returns a Result
pub fn obtain_user(credentials: UserCredentials) -> Result<User, UserError> {
/* ... */
}
And another module that declares handlers, this example handler obtains a user with some credentials and returns its JWT token if successful.
use actix_failwrap::proof_route;
use actix_web::{web::Form, HttpResponse, HttpResponseBuilder};
use crate::models::user::{UserError, UserCredentials, obtain_user};
// Route macro expands to #[actix_web::post("/login")] and allows
// to use `Result<HttpResponse, _>`.
#[proof_route("POST /login")]
async fn post_login(
// If the extractor (Form) fails, override it with MissingCredentials variant
#[error_override(MissingCredentials)] credentials: Form<UserCredentials>
) -> Result<HttpResponse, UserError> {
// Attempt to obtain the user; if it fails, propagate the error
let user = obtain_user(credentials.into_inner())?;
// On success, return a response with a "Login" header containing the JWT
Ok(
HttpResponse::Ok().
.insert_header(("Login", user.jwt()))
.finish()
)
}
ยงExported macros ๐ง
This crate exports two macros: ErrorResponse
and proof_route
.
ยง#[derive(ErrorResponse)]
Implements Into<actix_web::HttpResponse>
and Into<actix_web::Error>
for your thiserror
enums,
allowing direct propagation with the ?
operator in handlers.
[!WARNING] Requires
#[derive(thiserror::Error)]
because it uses theDisplay
implementation.
ยงSupported Attributes
-
#[default_status_code(...)]
Fallback status code used if a variant does not have its own#[status_code(...)]
Defaults toInternalServerError
(500). -
#[status_code(...)]
Sets the HTTP status code for a specific variant. Accepts a named status (e.g.BadRequest
) or number (400
). -
#[transform_response(fn)]
Customizes how the response is built. Takes a function of signature:fn(HttpResponseBuilder, String) -> HttpResponse
.
ยง#[proof_route(...)]
Simplifies route definition and error propagation.
#[proof_route("POST /path")]
Expands to:
#[actix_web::post("/path")]
An example function signature looks like
#[proof_route("GET /users")]
async fn get_users() -> Result<HttpResponse, Error> {}
[!TIP] You can use a
Result<T: actix_web::Responder, Error>
instead ofHttpResponse
.
Allows you to:
- Use
Result<HttpResponse, Error>
directly in route bodies. - Avoid importing
#[post]
,#[get]
, etc. individually. - Support extractor error override via
#[error_override(...)]
.
ยงSecurity ๐
Security is a top priority for us. If you believe youโve found a security vulnerability, do not open a public issue. Instead, please read our SECURITY.md policy and report it responsibly by contacting us at security@flaky.es.
ยงLicense ๐
This repository is dual licensed, If your repository is open source, the library is free of use, otherwise contact licensing@flaky.es for a custom license for your use case.
For more information read the license file.
Attribute Macrosยง
- proof_
route proof_route
Attribute Macro
Derive Macrosยง
- Error
Response ErrorResponse
Derive Macro