⚡ 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

AspectBenefitLimitation
ExtractorsAxum's type-safe extractors (Json, State, TypedHeader)Manual bridging to Bus required
Tower MiddlewareFull Tower ecosystem via Axum layersNot visible in Ranvier Schematic
State ManagementAxum's ergonomic State sharingState + Bus dual management
Business LogicRanvier transitions with Schematic visualizationExtra 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

Next Steps

Note: A complete Axum + Ranvier example is planned for a future milestone. For now, use the code snippets above as a starting point.

To learn more about integration patterns: