Axum Service Skill
Idiomatic Axum 0.8 web-service generation and review - an Agent Skill for Rust 1.97.0, Tokio 1.x, and serde 1.0.
What This Skill Does
Produces a deployable HTTP service checklist: workspace Cargo.toml, Router modules, shared AppState (PgPool, reqwest::Client), /health and /ready, tracing subscriber, thiserror domain errors mapped to status codes, and integration tests with tower::ServiceExt.
When to Invoke
- New microservice or BFF in a Rust workspace
- Reviewing PRs for blocking DB calls in async handlers
- Splitting a monolithic binary into routed modules
- Standardizing hackathon Axum code before production
Inputs
| Input | Why |
|---|---|
| Service name | Crate name, log fields, image tag |
| DB required? | sqlx::PgPool in state vs stub |
| Auth model | None, API key header, JWT middleware |
| Port | Default 3000 or platform convention |
| Workspace path | services/billing-api vs root crate |
Outputs
Cargo.tomlwithaxum,tokio,tower-http,tracing,serde, optionalsqlxsrc/main.rswithTcpListener, graceful shutdownsrc/routes/mod.rs,health.rs, domain routerssrc/error.rswithIntoResponsemappingtests/health.rsusingoneshotrequests- Verification command block
Guardrails
- Async handlers must not block - use
spawn_blockingor async DB only. - No secrets in generated files -
.env.exampleplaceholders only. - Health routes before domain routes - load balancers need them day one.
- Structured
tracing-#[instrument]on handlers; noprintln!in prod paths. - Clone cheap state -
Arc<PgPool>and reusedreqwest::Client. - Map errors to status - never leak internal strings on 500 in public APIs.
Recipe
cd services/billing-api
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo test
cargo run &
curl -sf http://localhost:3000/health
curl -sf http://localhost:3000/readyWorking Example (skeleton)
use axum::{routing::get, Router};
use std::net::SocketAddr;
use tokio::net::TcpListener;
mod routes;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();
let app = Router::new()
.merge(routes::health::router());
let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
let listener = TcpListener::bind(addr).await?;
axum::serve(listener, app).await?;
Ok(())
}FAQs
Axum or Actix for this service?
Axum when team standard is Tokio + Tower ecosystem. Actix only when ADR already selects it.
Include GraphQL scaffold?
Only when input specifies GraphQL day one; default REST routers.
OpenAPI generation?
Add utoipa in follow-up PR after routes stabilize.
Related
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.