⚡ Axum Integration Guide
Combine Axum's ergonomic API with Ranvier's Schematic workflows
Overview
Axum is a web framework that focuses on ergonomics and modularity, built on top of Tower. You can integrate Ranvier transitions with Axum handlers to combine:
- Axum: Type-safe extractors, state management, Tower middleware
- Ranvier: Schematic visualization, Bus propagation, composable logic
Integration Pattern
The recommended approach is to call Ranvier transitions from Axum handlers:
Example: Calling Ranvier Transition from Axum Handler
use axum::{{
extract::{{State, Json}},
http::StatusCode,
response::IntoResponse,
Router,
}};
use ranvier_core::{{Bus, Outcome}};
use ranvier_macros::transition;
use ranvier_runtime::Axon;
use std::sync::Arc;
// Shared app state
#[derive(Clone)]
struct AppState {{
pipeline: Axon<CreateUser, User, AppError, ()>,
}}
// Ranvier transition
#[transition]
async fn create_user(
input: CreateUser,
_res: &(),
bus: &mut Bus
) -> Outcome<User, AppError> {{
// Your business logic
Outcome::Next(User {{ id: 1, name: input.name }})
}}
// Axum handler
async fn create_user_handler(
State(state): State<Arc<AppState>>,
Json(payload): Json<CreateUser>
) -> impl IntoResponse {{
let mut bus = Bus::new();
match state.pipeline.execute(payload, &(), &mut bus).await {{
Outcome::Next(user) => (StatusCode::CREATED, Json(user)).into_response(),
Outcome::Fault(err) => (StatusCode::BAD_REQUEST, Json(err)).into_response(),
_ => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
}}
}}
// Axum app
#[tokio::main]
async fn main() {{
let pipeline = Axon::simple::<AppError>("create-user")
.then(create_user);
let state = Arc::new(AppState {{ pipeline }});
let app = Router::new()
.route("/users", axum::routing::post(create_user_handler))
.with_state(state);
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}}Example: Using Axum Extractors with Ranvier
use axum::{{
extract::{{Extension, TypedHeader}},
headers::Authorization,
headers::authorization::Bearer,
}};
async fn protected_handler(
TypedHeader(auth): TypedHeader<Authorization<Bearer>>, // Axum extractor
State(state): State<Arc<AppState>>,
Json(data): Json<RequestData>
) -> impl IntoResponse {{
let mut bus = Bus::new();
// Put extracted token in Bus for Ranvier transitions
bus.insert(auth.token().to_string());
match state.pipeline.execute(data, &(), &mut bus).await {{
Outcome::Next(result) => (StatusCode::OK, Json(result)).into_response(),
Outcome::Fault(err) => (StatusCode::UNAUTHORIZED, Json(err)).into_response(),
_ => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
}}
}}Example: Sharing State Between Axum and Ranvier
// Axum state includes both app config and Ranvier pipelines
#[derive(Clone)]
struct AppState {{
db: Arc<DatabasePool>,
order_pipeline: Axon<Order, OrderResult, AppError, ()>,
user_pipeline: Axon<CreateUser, User, AppError, ()>,
}}
// In handler, access both Axum state and execute Ranvier pipeline
async fn process_order(
State(state): State<Arc<AppState>>,
Json(order): Json<Order>
) -> impl IntoResponse {{
let mut bus = Bus::new();
// Put Axum-managed resources in Bus for Ranvier transitions
bus.insert(state.db.clone());
state.order_pipeline.execute(order, &(), &mut bus).await
// ... handle result
}}When to Use This Approach
✅ Good for:
- Existing Axum apps adding Ranvier for complex workflows
- Teams familiar with Axum's type-safe extractors
- Want Tower middleware with Ranvier business logic
- Need Ranvier's Schematic visualization for multi-step processes
⚠️ Consider:
- State bridging: You need to bridge Axum State to Ranvier Bus manually
- Middleware visibility: Axum middleware (Tower layers) won't appear in Ranvier Schematic
- Testing: Integration tests for Axum handlers, unit tests for Ranvier transitions
Trade-offs
| Aspect | Benefit | Limitation |
|---|---|---|
| Extractors | Axum's type-safe extractors (Json, State, TypedHeader) | Manual bridging to Bus required |
| Tower Middleware | Full Tower ecosystem via Axum layers | Not visible in Ranvier Schematic |
| State Management | Axum's ergonomic State sharing | State + Bus dual management |
| Business Logic | Ranvier transitions with Schematic visualization | Extra boilerplate for Axum ↔ Ranvier integration |
Comparison with Alternatives
- vs Pure Ranvier: Axum integration gives you type-safe extractors and Tower middleware, but loses some Schematic visibility
- vs Tower Integration: Axum provides higher-level ergonomics on top of Tower (extractors, routing)
- vs actix Integration: Axum uses type-based extractors, actix uses trait-based extractors