# Mir Backend Rust Axum

> Make It Right (Axum module). Axum + Tower + async Rust specific reliability augmentation. Use alongside mir-backend and mir-backend-rust when the target stack is Axum — it carries the mechanical footguns that the framework-agnostic tiers deliberately omit: extractor ordering (body-consuming extractors must be last), typed State<T> vs Extension<T> and the FromRef sub-state pattern, implementing In…

- **Type:** Skill
- **Install:** `agentstack add skill-anantbhandarkar-make-it-right-mir-backend-rust-axum`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [anantbhandarkar](https://agentstack.voostack.com/s/anantbhandarkar)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** Apache-2.0
- **Upstream author:** [anantbhandarkar](https://github.com/anantbhandarkar)
- **Source:** https://github.com/anantbhandarkar/make-it-right/tree/main/skills/mir-backend-rust-axum

## Install

```sh
agentstack add skill-anantbhandarkar-make-it-right-mir-backend-rust-axum
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# /mir-backend-rust-axum · Make It Right (Axum)

Bottom tier of the chain: `mir-backend` (generic gates) → `mir-backend-rust` (Tokio runtime model) → **this** (Axum/Tower library mechanics). Run the gates first; load the Rust runtime tier for blocking/cancellation/Arc concerns; reach for *this* at Gate 5 (design mechanics), Gate 6 (implementation), and Gate 7 review. **Runtime-level concerns (blocking the Tokio runtime, guard-across-await, cancellation safety, bounded channels, timeouts) live in `mir-backend-rust` — not here.**

**Stack assumed:** Axum (current stable, 0.7+) · Tower middleware · async Rust on Tokio. Notes apply to `axum` with `tokio` and `tower`/`tower-http`.

## The Axum footguns AI walks into most

### 1. Extractor order — body-consuming extractors MUST be last

This is the #1 Axum compile/runtime trap. HTTP request bodies are streams: **reading the body consumes it**. Axum extractors that consume the body (`Json`, `String`, `Bytes`, `Form`, `Multipart`) must be the **last parameter** in the handler signature. Only **one** body-consuming extractor per handler is allowed — the request has one body.

Extractors that do NOT consume the body (they read headers, URI, path, query, or app state) can appear in any position: `Path`, `Query`, `State`, `Extension`, `TypedHeader`, `ConnectInfo`.

```rust
// WRONG — Json is not last; compilation may succeed but the extractor
// that follows Json will see an empty/already-consumed body
async fn create_user(
    Json(payload): Json,  // consumes the body
    State(db): State,             // body already gone if Json is first
) -> impl IntoResponse { ... }

// RIGHT — body-consuming extractor is last
async fn create_user(
    State(db): State,             // reads app state, no body access
    Json(payload): Json,  // LAST — consumes the body here
) -> impl IntoResponse { ... }

// RIGHT — path + query before body
async fn update_item(
    Path(id): Path,
    Query(params): Query,
    State(db): State,
    Json(body): Json,     // LAST
) -> impl IntoResponse { ... }
```

AI routinely puts `State` last (habit from other frameworks) and bumps `Json` to first or second — flag any handler where a body extractor is not the final argument.

### 2. `State` vs `Extension` — prefer `State`, use `FromRef` for sub-states

Axum provides two mechanisms for injecting app-wide data into handlers:

- **`State`** (via `.with_state(value)` on the router): type-checked at compile time, zero-cost extraction, the idiomatic modern approach. Use this.
- **`Extension`** (via `.layer(Extension(value))`): stored as a type-erased map in the request extensions, extracted at runtime — a missing extension panics at runtime, not compile time. Use only for middleware-injected data (e.g. authenticated user injected by an auth layer downstream handlers expect).

For large apps with multiple shared resources (DB pool, config, cache), define a single `AppState` struct and implement `FromRef` for each sub-state so individual handlers can extract only what they need:

```rust
#[derive(Clone)]
struct AppState {
    db: PgPool,
    cache: RedisPool,
}

// Allow handlers to extract State directly from AppState
impl FromRef for PgPool {
    fn from_ref(state: &AppState) -> Self {
        state.db.clone()
    }
}

// Handler only declares what it needs
async fn get_user(
    State(db): State,     // extracted via FromRef
    Path(id): Path,
) -> impl IntoResponse { ... }

// Router wired to the full AppState
let app = Router::new()
    .route("/users/:id", get(get_user))
    .with_state(AppState { db, cache });
```

`AppState` must implement `Clone` (cheaply — wrap expensive resources in `Arc` or use pool handles that are already `Clone`).

### 3. Error handling — implement `IntoResponse`, never unwrap in handlers

Handlers return `Result` where `E: IntoResponse`. **Do not `.unwrap()` or `.expect()` inside a handler** — a panic kills the task and returns a 500 with no body (or crashes the process depending on panic handler). Implement `IntoResponse` for your error type to produce consistent, controlled HTTP responses:

```rust
#[derive(Debug)]
enum AppError {
    NotFound(String),
    DbError(sqlx::Error),
    Unauthorized,
}

impl IntoResponse for AppError {
    fn into_response(self) -> Response {
        let (status, message) = match self {
            AppError::NotFound(msg)  => (StatusCode::NOT_FOUND, msg),
            AppError::DbError(e)     => {
                tracing::error!("db error: {e}");
                (StatusCode::INTERNAL_SERVER_ERROR, "internal error".into())
            }
            AppError::Unauthorized   => (StatusCode::UNAUTHORIZED, "unauthorized".into()),
        };
        (status, Json(json!({ "error": message }))).into_response()
    }
}

async fn get_user(
    State(db): State,
    Path(id): Path,
) -> Result, AppError> {
    let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", id as i64)
        .fetch_optional(&db)
        .await
        .map_err(AppError::DbError)?
        .ok_or_else(|| AppError::NotFound(format!("user {id} not found")))?;
    Ok(Json(user))
}
```

Using `anyhow::Error` as the error type with a blanket `impl IntoResponse` is also valid for rapid development, but always ensure internal error details are not leaked to clients in production.

### 4. Tower middleware layer order — outermost wraps first, responses last

Tower middleware is composed as a **stack**: the layer added **outermost** (last in the builder chain or added first to `ServiceBuilder`) sees the **request first** and the **response last**. This is the reverse of what many frameworks call "middleware order."

```rust
let app = Router::new()
    .route("/", get(handler))
    .layer(
        ServiceBuilder::new()
            .layer(TraceLayer::new_for_http())     // outermost: sees req first, resp last
            .layer(TimeoutLayer::new(Duration::from_secs(10)))
            .layer(CompressionLayer::new())
            .layer(auth_layer)                     // innermost: sees req last, resp first
    );
```

Practical ordering rules:
- **Tracing/logging** — outermost so it captures the full round trip including all other middleware latency.
- **Timeout** — before auth/business logic so runaway requests are cut regardless of what's inside.
- **Auth/authorization** — inner, after trace so requests are logged even if they fail auth (useful for security auditing), before business handlers.
- **Compression** — innermost (wraps the response body), since it should apply to the final handler output.

AI frequently reverses the intuition ("last added = outermost") because it matches neither Express nor Django middleware mental models. When reviewing a `.layer()` chain, trace through request and response direction explicitly.

## How this slots into the core pipeline

- **Gate 5 (Design):** state handler signatures with correct extractor order; define `AppState` with `FromRef` sub-states; declare the error type implementing `IntoResponse`; sketch the Tower middleware stack with ordering rationale.
- **Gate 6 (Implementation):** body-consuming extractor last; use `State` not `Extension` for app state; `?` propagation through `AppError: IntoResponse`; check middleware layer ordering in the `ServiceBuilder` chain.
- **Gate 7 (Review):** verify extractor ordering in every handler; confirm no `unwrap` in handlers; confirm `State` vs `Extension` usage; trace middleware request/response direction.

## Edit boundary (what belongs here vs. above/below)

**This module holds ONLY Axum + Tower library mechanics.** Apply the 3-tier placement test before adding anything:

- True for Go/Python/Node too (idempotency, invariants, gates, observability)? → **generic core** (`mir-backend`).
- True for every async Rust backend on Tokio (blocking, guard-across-await, cancellation, Arc/'static, channels, timeouts)? → **runtime tier** (`mir-backend-rust`).
- A mechanical footgun of *this library* (extractor order, `State` vs `Extension`, `FromRef`, `IntoResponse`, Tower layer ordering)? → **here**.
- A *different* Rust framework (Actix-web, Warp) → its own `mir-backend-rust-` module. Never widen this one.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [anantbhandarkar](https://github.com/anantbhandarkar)
- **Source:** [anantbhandarkar/make-it-right](https://github.com/anantbhandarkar/make-it-right)
- **License:** Apache-2.0

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-anantbhandarkar-make-it-right-mir-backend-rust-axum
- Seller: https://agentstack.voostack.com/s/anantbhandarkar
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
