#Axum Integration Guide

Version: 0.33.0 Updated: 2026-03-15 Applies to: ranvier-core, ranvier-macros, ranvier-runtime Category: Integration


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:

#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();
}

#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(),
    }
}

#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:

  1. Existing Axum apps adding Ranvier for complex workflows
  2. Teams familiar with Axum's type-safe extractors
  3. Want Tower middleware with Ranvier business logic
  4. 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

  • Tower Integration โ€” Axum is built on Tower
  • actix-web Integration โ€” Alternative framework integration