Rust Axum Agent Rules
Project Context
You are building a web API with Axum, Tokio, and the Tower ecosystem. Axum leverages Rust's type system to make handlers and extractors composable and misuse-resistant.
Code Style & Structure
- Run `cargo fmt` and `cargo clippy -- -D warnings` before every commit.
- Write `///` doc comments for all public items; include examples for extractors and handler signatures.
- Organize source into modules by domain: `routes/users.rs`, `routes/auth.rs`, not by layer type.
- Use `AppState` as the single shared state type, wrapped in `Arc<AppState>` and passed via `State<Arc<AppState>>`.
- Keep handler functions under 30 lines; delegate business logic to a service layer.
Routing & Application Setup
- Build the router with `Router::new().route("/users", get(list_users).post(create_user))`.
- Use `Router::nest("/api/v1", api_router)` to version the API and group related routes.
- Apply global middleware with `Router::layer(ServiceBuilder::new().layer(...).into_inner())`.
- Apply route-specific middleware with `.route_layer(...)` — it runs only for matched routes, not 404s.
- Use `Router::with_state(Arc::new(AppState::new(...)))` to inject state at the application root.
Extractors
- Use `Path<T>`, `Query<T>`, `Json<T>`, and `State<S>` as function parameters — Axum resolves them automatically.
- Implement `FromRequestParts` for authentication extractors that read headers without consuming the body.
- Implement `FromRequest` for body extractors that consume the request body exactly once.
- Return `Result<T, E>` from `FromRequest` implementations where `E: IntoResponse` to produce typed rejection errors.
- Use `extension::Extension<T>` for request-scoped data injected by middleware (e.g., request ID, user identity).
Middleware
- Compose middleware with `tower::ServiceBuilder` — order from outer to inner: tracing, compression, auth, rate limit.
- Use `tower_http::trace::TraceLayer` to attach structured tracing spans to every request automatically.
- Use `tower_http::cors::CorsLayer` with explicit origin configuration — never `AllowOrigin::any()` in production.
- Write custom Tower middleware by implementing `tower::Layer` and `tower::Service`; use `tower::util::MapResponseLayer` for simple response transformations.
- Propagate request IDs through all middleware layers by injecting them into tracing spans and response headers.
Error Handling
- Define an `AppError` enum with `thiserror::Error`; implement `IntoResponse` to map each variant to a status code and JSON body.
- Use `anyhow::Result` inside services; convert to `AppError` at the handler boundary with `map_err(AppError::from)`.
- Return `(StatusCode, Json<ErrorBody>)` from the `IntoResponse` implementation for a consistent JSON error shape.
- Never expose raw database errors, file paths, or stack traces in the response body.
- Log error details with `tracing::error!` inside the `IntoResponse` implementation before serializing the client-safe message.
Database (SQLx)
- Share a `PgPool` via `Arc<AppState>` — initialize it once at startup with `PgPoolOptions::new()`.
- Use `sqlx::query_as!` for compile-time query checking; supply the return type as the first generic parameter.
- Run migrations at startup with `sqlx::migrate!()` so the schema is always up to date.
- Use `pool.begin().await` for transactions; call `tx.commit().await` on success and let `Drop` handle rollback on early return.
- Map database row types to domain types at the repository layer — never return `sqlx::Row` from a service function.
Type Safety
- Define newtype wrappers for domain identifiers: `struct UserId(Uuid)` with `#[derive(Deserialize, Serialize)]`.
- Use enums to represent finite state: payment status, user role, order lifecycle — make invalid states unrepresentable.
- Validate inputs at the extraction boundary using `validator` crate with `#[validate(...)]` attributes on request structs.
- Use `serde(deny_unknown_fields)` on request structs to reject payloads with unexpected keys.
Testing
- Write integration tests in `tests/` using `axum::test::TestClient` or spawn the app with a `TcpListener` and use `reqwest`.
- Create a `test_app()` helper that builds the `Router` with a test database from `testcontainers`.
- Write unit tests for extractors and service functions inside `#[cfg(test)]` modules.
- Assert on both the HTTP status code and the deserialized JSON response body in every handler test.
Performance
- Profile with `cargo flamegraph` before optimizing; target allocation-heavy paths first.
- Use `Bytes` for zero-copy body handling inside custom extractors and response builders.
- Enable response compression with `tower_http::compression::CompressionLayer` for text-heavy endpoints.
- Configure `PgPoolOptions::max_connections` based on observed load — default of 10 is too low for production.