SuperAPI AI Directory

axum

Coding & development

View repository

Updated 10-15 20:31

About this skill

Axum (Rust)

Axum is routing + extractors on top of Tower/Hyper/Tokio. Small handlers, explicit state, composition via Router and Tower middleware.

Typical stack

Crate Role
axum Router, extractors, response
tokio Async runtime
tower / tower-http Middleware (trace, cors, compression, timeout)
serde JSON / form
tracing Structured logs
DB sqlx or ORM already in the project

Preserve the existing stack. Do not impose new ORMs without being asked. Runtime: tokio. Logs: tracing. HTTP client: reqwest. ORM: sea-orm / diesel if the repo already uses it; otherwise sqlx.

Organization

src/
  main.rs          # boot, config, serve
  lib.rs           # app() testável
  routes/          # Router por domínio
  handlers/
  extractors/      # AuthUser, Pagination, etc.
  error.rs         # AppError + IntoResponse
  state.rs         # AppState
  services/
  • app() returns Router for tests without socket bind.
  • Routes by domain; main only configures and serves.
  • State in State<AppState> (Arc internal when needed).

Non-negotiable rules

  • Handlers async returning impl IntoResponse or Result<T, AppError>.
  • Input via typed extractors (Json, Query, Path, State, custom).
  • One AppError central with HTTP status and stable body.
  • Authn/authz on the server (extractor/middleware), never only on the client.
  • Conscious timeouts and body limits.
  • Do not block the Tokio runtime in handlers.

Routing

Router::new()
    .nest("/api/v1", api_router)
    .route("/health", get(health))
    .layer(TraceLayer::new_for_http())
    .with_state(state)
  • Group by prefix (nest / merge).
  • Auth middleware in Router nested, not on every public route.
  • Explicit method routing: get, post, put, patch, delete.

Extractors

Order matters: extractors consume parts of the request.

  • Path<T>, Query<T>, Json<T>, Form<T>
  • State<S>, Extension<T> (prefer State for app state)
  • Custom extractor for AuthUser / CurrentUser
  • Extractor rejections → 4xx response consistent with AppError

Validate after decode (size, ranges, enums). Serde does not replace business rules.

State and dependencies

#[derive(Clone)]
struct AppState {
    db: PgPool,
    // configs baratas / Arc<Config>
}
  • Cheap clone (handles, Arc).
  • No mutable globals; no lazy_static for DB.
  • Services receive what they need (pool, clients), not the whole Request .

Errors

enum AppError {
    NotFound,
    Unauthorized,
    Forbidden,
    Validation(String),
    Internal(anyhow::Error),
}

impl IntoResponse for AppError { /* status + JSON { code, message } */ }
  • 4xx for client; 5xx for internal failures.
  • Log Internal with tracing; generic external message.
  • Do not leak Debug from anyhow to the client in production.

Tower Middleware

Use tower-http for the common cases:

  • TraceLayer, CorsLayer, CompressionLayer, TimeoutLayer
  • RequestBodyLimitLayer / default body limits
  • SetSensitiveHeadersLayer for Authorization

Custom middleware: from_fn / Tower services when you need auth or correlation id.

Tests

  • tower::ServiceExt::oneshot or project helpers over Router.
  • Isolated test DB or mocks at the infrastructure edge.
  • Cover 200, 401, 403, 404, validation.

Anti-patterns

  • Monolithic business logic in main.rs
  • unwrap in handlers
  • Sharing Request parts between extractors in a fragile way
  • CORS * with cookies/credentials
  • Ignoring cancellation/timeouts in internal HTTP clients
  • Mixing Actix/Rocket patterns unnecessarily

Integration

  • Language and crates: rust
  • Runtime: tokio
  • Languages: rust-best-practices
  • Production: rust-axum-production
  • Logs: tracing
  • Client: reqwest
  • WS: rust-axum-websocket
  • Contracts: api-design
  • i18n: i18n-backend

Completion criteria

  • Router composable and testable via app()
  • Consistent extractors and AppError consistent
  • Authz on sensitive routes
  • Trace/timeout/limits middleware when applicable
  • Tests of critical flows passing