Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Axum and SQLx interoperability guide

Rullst builds on Axum, Tokio, Tower and SQLx and preserves direct access to their APIs. This reduces coupling for HTTP and database code; it does not make every optional framework subsystem free to remove.

Mount an existing Axum router

rullst::Router supports conversion to and from axum::Router:

#![allow(unused)]
fn main() {
use axum::{Router as AxumRouter, routing::get};
use rullst::Router;

async fn existing_handler() -> &'static str {
    "existing Axum route"
}

let existing = AxumRouter::new().route("/existing", get(existing_handler));
let rullst_router: Router = existing.into();
let axum_router: AxumRouter = rullst_router.into();
let _ = axum_router;
}

The standard Axum extractor and response types remain available, including through Rullst’s documented re-exports. Generated projects do not depend on axum directly: add axum = "0.8" (the version Rullst uses) to run this example as written, or import the same types through rullst::web::axum.

Keep raw SQLx where it is useful

Application-owned SQL can live beside generated ORM queries. Bind every dynamic value and review structural SQL separately:

#![allow(unused)]
fn main() {
use sqlx::PgPool;

async fn active_names(pool: &PgPool) -> Result<Vec<String>, sqlx::Error> {
    sqlx::query_scalar("SELECT name FROM users WHERE active = $1")
        .bind(true)
        .fetch_all(pool)
        .await
}
}

Generate an escape-hatch template

cargo rullst eject

The command writes a reviewable Axum/Tokio entry-point template to src/ejected_main.rs (--output src/<file>.rs chooses another file below src/) and leaves src/main.rs unchanged. The template is a placeholder route behind the configured Rullst security baseline, compiled through the rullst::web::axum re-export. It does not copy the application’s routes; ORM models, migrations, authentication policy, Studio/Nexus integration and other selected subsystems can still require deliberate migration work.

cargo rullst eject --force writes the template over src/main.rs instead and keeps the original at src/main.rs.rullst-backup (it refuses to run while that backup exists). Use it only with a committed or backed-up worktree, move the module declarations and routes from the backup into application_routes(), then run cargo check.

Practical migration sequence

  1. Keep domain types and handlers independent of framework globals.
  2. Convert router boundaries incrementally.
  3. Replace generated ORM calls with raw SQLx only where that trade-off helps.
  4. Inventory authentication, middleware order, jobs, cache and admin surfaces.
  5. Run application-specific integration and authorization tests before removing dependencies.

Interoperability is a maintained design goal. “Zero lock-in” or zero migration cost is not a framework guarantee.