1---2name: rust-backend3description: Use for Rust backend services — Axum/Actix-web, Tokio async, thiserror/anyhow, SQLx/SeaORM, testing, hardening. Triggers — Cargo.toml, .rs handlers, 'axum', 'actix', 'tokio', 'sqlx'.4---56# Rust Backend Development78## When to use9- Writing HTTP APIs or gRPC services with Axum, Actix-web, or Warp10- Designing ownership-safe data models and async service layers11- Implementing error types with `thiserror` and propagation with `anyhow`/`?`12- Integrating databases via `sqlx` (async, compile-time checked) or SeaORM13- Writing unit, integration, and property-based tests14- Profiling CPU/allocation with `perf`, `flamegraph`, or `criterion`1516## Workflow17181. **Confirm Rust edition and MSRV** — check `Cargo.toml` (`edition = "2021"`, `rust-version`). Use stable toolchain unless a nightly feature is justified and documented.192. **Establish crate/module layout**:20 ```21 src/22 main.rs # binary entrypoint: init tracing, DB pool, router, server23 lib.rs # re-exports for integration tests24 api/ # Axum handlers, extractors, middleware25 services/ # business logic (pure functions over domain types)26 db/ # sqlx queries, repository impls27 domain/ # types, enums, value objects (no I/O)28 errors.rs # AppError enum + IntoResponse impl29 config.rs # typed config from env vars30 ```313. **Define domain types and the `AppError` enum first** — they drive everything else.32 ```rust33 #[derive(Debug, thiserror::Error)]34 pub enum AppError {35 #[error("not found")] NotFound,36 #[error("db: {0}")] Db(#[from] sqlx::Error),37 }38 ```394. **Set up the `AppState`** — `#[derive(Clone)]` struct holding `PgPool`, config, and shared clients. Pass via `axum::Extension` or state extractor.405. **Write handlers** — thin: extract → call service → return `impl IntoResponse`. Validation via `validator` crate + custom extractor.416. **Async runtime** — `#[tokio::main]` with `tokio::runtime::Builder` for production (configure worker threads). Never `block_on` inside async code.427. **Database queries with `sqlx`**:43 - Use `sqlx::query_as!` macros — compile-time checked against a live DB (`DATABASE_URL` in `.env`).44 - All multi-step writes in explicit `pool.begin()` transactions.45 - Set `connect_options.statement_cache_capacity` and pool max connections.468. **Write tests**:47 - Unit: pure functions, no I/O, in `#[cfg(test)]` modules within the same file.48 - Integration: `tokio::test`, real DB via `sqlx::test` attribute (creates an isolated DB per test).49 - Use `axum::body::to_bytes` + `serde_json` to assert handler responses.509. **Benchmark hot paths** with `criterion`; run `cargo flamegraph` to visualise.5110. **Audit** against .claude/checklists/security.md and .claude/checklists/performance.md.5253## Standards5455### Ownership and types56- Prefer `&str` over `String` in function parameters where the callee doesn't need ownership.57- Use `Arc<T>` for shared state across async tasks; `Mutex<T>` only inside synchronous critical sections — prefer `tokio::sync::Mutex` in async contexts.58- Newtype pattern for domain IDs: `struct UserId(Uuid)` prevents accidental ID mixups.59- Derive `Debug`, `Clone`, `PartialEq` for domain types; derive `Serialize`/`Deserialize` only at API boundary structs.6061### Error handling62- Define one `AppError` enum per crate/service with `thiserror`.63- Use `?` for propagation; never `.unwrap()` or `.expect()` in production code paths.64- Implement `axum::response::IntoResponse` for `AppError` to map to HTTP status + JSON body.65- Log errors at the point of origin; propagate the type, not a string.6667### Async / Tokio68- CPU-bound work: `tokio::task::spawn_blocking` or `rayon` threadpool — never block in an async context.69- Set timeouts with `tokio::time::timeout` on every external call.70- Structured concurrency: `tokio::select!`, `JoinSet`, or `FuturesUnordered` over bare `tokio::spawn` when you need to collect results.7172### Security73- Validate all input with the `validator` crate and reject early with 422.74- Hash passwords with `argon2` (use `argon2` crate, not raw `bcrypt`).75- Use `secrecy::Secret<String>` for tokens and passwords to prevent accidental `Debug` leakage.76- Set `Content-Security-Policy`, `X-Frame-Options`, and security headers via `tower-http::set-header`.7778### Do not79- Do not use `unsafe` without a `SAFETY:` comment block explaining the invariants upheld.80- Do not panic in library code (`panic!`, `unwrap`, `expect`) — return a `Result` instead.81- Do not use `.clone()` reflexively to satisfy the borrow checker — restructure first.82- Do not place `#[allow(unused_*)]` globally; fix the warnings instead.8384## Common mistakes to avoid8586| Mistake | Fix |87|---|---|88| `async fn` that holds a non-`Send` type across `.await` | Restructure to drop the non-Send value before the await point. |89| `sqlx::query!` failing at runtime due to missing `DATABASE_URL` at compile time | Set `DATABASE_URL` in `.env` and run `cargo sqlx prepare` to embed offline metadata. |90| Cloning `PgPool` repeatedly into handlers | `PgPool` is already `Clone + Send + Sync` cheaply (Arc-backed); clone freely. |91| Shadowing outer error types with `anyhow::Error` | Use `thiserror` for typed errors in libraries; `anyhow` only in binaries/tests. |92| `Mutex<Vec<T>>` contention under load | Use `DashMap` or channel-based message passing for concurrent collections. |93| Missing `tower::ServiceBuilder` middleware order | Middleware applies inside-out; put logging outermost, auth innermost. |9495## Output format9697- New handler: `async fn` with typed extractors, service call, and `AppError` propagation.98- Error enum: `thiserror` enum with `#[from]` conversions and `IntoResponse` impl.99- `Cargo.toml` additions: feature-flagged dependencies with version pinning.100- Test: `#[sqlx::test]` annotated async function with setup, action, and assertion.101102## Related checklists103- .claude/checklists/security.md104- .claude/checklists/performance.md105- .claude/checklists/qa.md106107## Related agents108- .claude/agents/core/orchestrator.md109- .claude/agents/engineering/devops-engineer.md