Axtra
🎨 Overview
Opinionated helpers for Axum + Astro projects.
Warning: This library is experimental, opinionated, and subject to breaking changes.
🐉 Here be dragons 🐉
Features
Error Handling
-
AppError
AppError: One error type for all your Axum APIs.- Error variants for authentication, authorization, validation, database, not found, and more.
- Automatic handling of thrown WithRejection<Json> and Validation using
validator
-
Error Macros
app_error!macro: Ergonomic error construction for all variants.- Captures error location automatically.
- Supports underlying error mapping for easy use with
.map_err().
-
TypeScript Type Generation
- Rust error types (
ErrorCode, validation errors, etc.) exported to TypeScript viats-rs. - Ensures your frontend and backend share error contracts.
- Rust error types (
-
Error Notifications
- Send critical errors to a variety of services/notifiers
- Sentry integration (optional)
- Slack integration (optional)
- Discord integration (optional)
- Send critical errors to a variety of services/notifiers
Api Responses
- Wrapped JSON Responses
WrappedJson<T>: Automatically wraps responses with a key derived from the type name.ResponseKeyderive macro: Customize or auto-generate response keys for your types.- List responses are pluralized automatically.
Axum Helpers
-
Health Check Endpoint
- Built-in Axum route for checking Postgres connectivity.
- Returns status, DB health, and timestamp.
-
Static File Serving
- Helpers for serving SPA and static files with Axum.
Bouncer
- IP Banning and Malicious Path Filtering
- Simple IP banning and malicious path filtering middleware for Axum.
Notifications
- Notification Integration
- Slack and Discord error notifications
ErrorHandling
AppError
AppError is an enum type containing types for handling BadRequest, NotFound, Authorization, Authentication, Database and Exception errors.
AppErrors will be logged automatically with the following severity
| Error Type | Severity (Log Level) |
|---|---|
| Authentication | INFO |
| Authorization | INFO |
| BadRequest | WARN |
| NotFound | WARN |
| Validation | WARN |
| Database | ERROR |
| Exception | ERROR |
- INFO: Expected authentication/authorization failures.
- WARN: Client errors, invalid input, not found, validation issues.
- ERROR: Server-side failures, database errors, exceptions, and triggers notifications (Slack/Discord/Sentry if enabled).
WithRejection
AppError will automatically handle malformed JSON and render BadRequest errors when using WithRejection.
pub async : ,
)
Validator
When using .validate()? in a handler, AppError will automatically catch and render the ValidationErrors.
handler
Error Macro Usage
The app_error! macro makes error construction ergonomic and consistent.
It automatically tracks error location and supports both direct and closure-based usage.
If no response type is passed, defaults to HTML.
Standalone (Direct) Usage
// Basic error (HTML response)
Err;
// JSON error
Err;
// HTML error
Err;
// Not found resource: &str
Err;
Err;
// Unauthorized resource: &str, action: &str
Err;
Err;
// Unauthenticated
Err;
Err;
// Validation error
Err;
Err;
// Thrown exceptons
Err
Err
Err
Closure (For .map_err() and error mapping)
// Bad request with underlying error
let value: i32 = input.parse.map_err?;
// With format args
let value: i32 = input.parse.map_err?;
// JSON error with underlying error
let user: User = from_str.map_err?;
// Database error mapping
let user = query!
.fetch_one
.await
.map_err?;
// Exception mapping
let result = do_something.map_err?;
Typescript types
Axtra provides Ts-Rs bindings to output typed ErrorResponses.
To enable, add the export to your build.rs
use ErrorResponse;
use fs;
use Path;
use TS;
/**
* Enum of all possible error codes.
*/
export type ErrorCode = "authentication" | "authorization" | "badRequest" | "database" | "exception" | "notFound" | "validation";
export type ErrorResponse = { status: string, message: string, code: ErrorCode, validationErrors?: SerializableValidationErrors, };
/**
* Represents all validation errors in a serializable form.
*/
export type SerializableValidationErrors = { errors: Array<ValidationFieldError>, };
/**
* Represents a single field validation error.
*/
export type ValidationFieldError = { field: string, code: string, message: string, params: { [key in string]?: string }, };
Error Notification Feature Flags
Axtra supports sending critical errors to external services for alerting and monitoring.
Enable these features in your Cargo.toml as needed:
sentry
- Purpose:
Automatically sendsDatabaseandExceptionerrors to Sentry for error tracking. - How to use:
Enable the feature:
Configure Sentry in your app (see sentry docs).toml features = ["sentry"] - Effect:
When enabled, critical errors are reported to Sentry in addition to being logged.
notify-error-slack
- Purpose:
Sends critical errors (database, exception, throw) to a Slack channel via webhook. - How to use:
Enable the feature:
Set your webhook URL:toml features = ["notify-error-slack"]SLACK_ERROR_WEBHOOK_URL=your_webhook_url - Effect:
When enabled, errors are posted to Slack using the configured webhook.
notify-error-discord
- Purpose:
Sends critical errors (database, exception, throw) to a Discord channel via webhook. - How to use:
Enable the feature:
Set your webhook URL:toml features = ["notify-error-discord"]DISCORD_ERROR_WEBHOOK_URL=your_webhook_url - Effect:
When enabled, errors are posted to Discord using the configured webhook.
Note:
All notification features are opt-in and only send alerts for server-side errors (Database, Exception, or throw).
You can enable any combination of these features as needed for your project.
Api Responses
WrappedJson & ResponseKey
Axtra provides a convenient way to wrap API responses with a predictable key, using the WrappedJson<T> type and the ResponseKey derive macro.
Usage
use ;
use Serialize;
// In your handler:
async
Produces JSON:
Customizing the Response Key
You can override the default key by using the #[response_key = "custom_name"] attribute:
Produces JSON:
List Responses
When returning a list, the key is automatically pluralized:
async
Produces JSON:
Macro Implementation
// #[derive(ResponseKey)] will auto-implement this trait:
See axtra_macros::ResponseKey for details.
Axum Helpers
Health Check Endpoint
Axtra provides a ready-to-use health check route for monitoring your application's status and database connectivity.
Usage
use check_health;
use ;
use PgPool;
Response (healthy):
Response (degraded):
- Returns HTTP 503 Service Unavailable if the database is not reachable.
Static File & Single Page App (SPA) Routes
Axtra includes helpers for serving static files and SPAs (such as Astro or React) with Axum.
Serve a Single Page App (SPA)
use serve_spa;
use Router;
// Serves files from ./dist/myapp/index.html for /myapp and /myapp/*
let router = new.merge;
Serve Static Files
use serve_static_files;
use Router;
// Serves files from ./dist, with compression and custom cache headers
let router = new.merge;
- Requests to
/and other paths will serve files from the./distdirectory. - Requests for missing files will return
404.htmlfrom the same directory. - Cache headers are set for
_staticand_astroassets for optimal performance.
See routes/health.rs and routes/astro.rs for full implementation details.
Bouncer: IP Banning & Malicious Path Filtering
Axtra's bouncer middleware automatically bans IP addresses that hit known malicious or unwanted paths, helping protect your Axum app from common scanner and exploit traffic.
Enable the bouncer feature in your Cargo.toml to access the Notifier API:
Features
- Ban IPs for a configurable duration when they access blocked paths.
- Use presets (
"wordpress","php","config") or custom paths for filtering. - Customize HTTP status for banned and blocked responses.
- Set log level for event tracing (
trace,debug,info, etc). - Expose the banlist for observability and monitoring.
Usage Example
use ;
use ;
use StatusCode;
use Level;
use Duration;
// Create a config with presets and custom paths, and customize responses/logging
let config = from_rules
.duration
.banned_response
.blocked_response
.log_level;
let layer = new;
let app = new
.route
.layer;
Presets
Available presets for common hacker/scanner paths:
"wordpress""php""config"
Advanced Usage
You can also pass only presets or only custom paths:
let config = from_preset_rules;
let config = from_custom_rules;
Tracing & TraceLayer Integration
The bouncer middleware uses tracing to log blocked and banned events.
You can configure the log level via .log_level(Level::DEBUG) or similar.
Best Practice:
Place BouncerLayer before Axum's TraceLayer so that blocked/banned requests are logged by bouncer and not missed by TraceLayer's on_response hooks.
Example with TraceLayer
use ;
use ;
use TraceLayer;
let config = from_rules
.log_level;
let app = new
.route
.layer
.layer;
Logging:
- Bouncer logs blocked/banned events at your chosen level.
- TraceLayer logs all requests that reach your handlers.
- Blocked/banned requests are handled and logged by bouncer, and do not reach TraceLayer's
on_response.
See bouncer/mod.rs and bouncer/layer.rs for full implementation details.
Notifier
Axtra includes a flexible notification system for sending error alerts to Slack and Discord.
Enable the notifier feature in your Cargo.toml to access the Notifier API:
[]
= []
You can then use the Notifier struct to send messages to Slack and Discord webhooks.
Notifier API
use Notifier;
use json;
// Create a notifier for Slack
let slack = with_slack;
// Send a simple Slack message
slack.notify_slack.await?;
// Send a rich Slack message (blocks)
let blocks = json!;
slack.notify_slack_rich.await?;
// Create a notifier for Discord
let discord = with_discord;
// Send a simple Discord message
discord.notify_discord.await?;
// Send a rich Discord message (embeds)
let embeds = json!;
discord.notify_discord_rich.await?;
You can also use static methods for one-off notifications:
use Notifier;
use json;
// Send a one-off Slack message
slack.await?;
// Send a one-off rich Slack message (blocks)
let blocks = json!;
slack_rich.await?;
// Send a one-off Discord message
discord.await?;
// Send a one-off rich Discord message (embeds)
let embeds = json!;
discord_rich.await?;
See notifier/mod.rs for full API details.
Documentation
License
MIT
Contributing
PRs and issues welcome! See CONTRIBUTING.md.