Skip to content
STACK IT FAST

Rust + Axum + PostgreSQL API (sqlx & Tokio)

Curated rule Developer Tool & API · Data & Storage Infra Updated Oct 2026 Which file does my tool read?
rust-axum-postgres.md

Rust API and backend rules: Axum 0.8 on Tokio, compile-time SQL checks with sqlx 0.9, Tower middleware, typed errors and a cargo check loop for AI agents.

Formats
4 files
AGENTS.md
50 lines
CLAUDE.md
18 lines
Languages
Rust
Updated
Oct 2026
Used by
4 projects
Install

Writes .claude/skills/rust-axum-postgres/SKILL.md

$ curl -s --create-dirs -o .claude/skills/rust-axum-postgres/SKILL.md https://stackitfast.com/rules/rust-axum-postgres/SKILL.md

Rule files

AGENTS.md· 50 lines · 3.8 KB
1# Project Architecture & Guidelines (Rust + Axum + PostgreSQL)
2
3## 1. System Architecture
4- **Language & Edition**: Rust 2024 edition, stable toolchain pinned in `rust-toolchain.toml`.
5- **Async Runtime**: Tokio (`tokio = { version = "1", features = ["full"] }`).
6- **Web Framework**: Axum 0.8 with Tower service ecosystem.
7- **Database & Persistence**: PostgreSQL accessed via `sqlx` (`sqlx = { version = "0.9", features = ["runtime-tokio", "tls-rustls", "postgres", "macros", "migrate", "uuid", "chrono"] }`).
8- **Serialization**: `serde` and `serde_json`.
9
10## 2. Project Layout & Modular Separation
11- `src/main.rs`: Runtime initialization, configuration loading, database pool creation, and router binding.
12- `src/routes/`: Route handlers split by resource domain (e.g. `users.rs`, `health.rs`).
13- `src/models/`: Domain structs deriving `Serialize`, `Deserialize`, and `sqlx::FromRow`.
14- `src/db/`: Database connection pool management and migration runners (`sqlx::migrate!()`).
15- `src/error.rs`: Unified `AppError` enum implementing `axum::response::IntoResponse`.
16
17## 3. Handlers & Extractors
18- Use Axum extractors (`State(pool)`, `Json(payload)`, `Path(id)`, `Query(params)`) in strict order (body extractors last).
19- Manage application state via `axum::extract::State<Arc<AppState>>`.
20- Validate all inbound requests using validator crates or custom domain constructors (`NewUser::validate()`).
21
22## 4. Database & sqlx Compile-Time Checks
23- Favor `sqlx::query_as!` and `sqlx::query!` macros to validate SQL syntax and types against the live database at compile time.
24- Always configure connection pooling: `PgPoolOptions::new().max_connections(20).acquire_timeout(Duration::from_secs(3))`.
25- Place migrations in `migrations/` folder as sequential `.sql` files (`YYYYMMDDHHMMSS_name.sql`).
26
27## 5. Error Handling & Observability
28- Never `unwrap()` or `panic!()` in route handlers. Propagate errors with the `?` operator into custom `AppError`.
29- Implement `IntoResponse` for `AppError` to emit appropriate HTTP status codes (400, 401, 404, 500) and structured JSON bodies `{ "error": "message" }`.
30- Integrate `tracing` and `tracing-subscriber` for structured JSON logging and distributed span tracking.
31
32## 6. Testing Conventions
33- Use `#[tokio::test]` for async integration tests; spin up a real Postgres instance with `testcontainers` rather than mocking `sqlx`.
34- Run `cargo sqlx prepare` in CI to verify the offline query cache (`.sqlx/`) stays in sync with actual schema — a stale cache silently masks broken queries.
35- Test `AppError` → HTTP status mapping explicitly; a wrong status code on an error path is a common silent regression.
36- `cargo clippy -- -D warnings` treated as a test failure, not a style nit — Rust's linter catches real bugs (unused Results, needless clones).
37
38## 7. Agent Loop (run after every change)
391. `cargo check` until clean; read the full compiler message, including the `help:` lines, before editing.
402. `cargo clippy --all-targets -- -D warnings`.
413. `cargo test`.
424. After changing a `sqlx::query!`, run `cargo sqlx prepare` so the offline cache matches.
43- Fix ownership errors by restructuring (borrow, move, `Arc` for shared state), not by cloning large values or reaching for `unsafe`.
44- Do not add a crate without stating why; prefer what is already in `Cargo.toml`.
45
46## 8. Git Workflow & PR Conventions
47- Conventional Commits (`feat:`, `fix:`, `refactor:`) scoped to the module, e.g. `fix(routes/users): return 404 instead of 500 on missing user`.
48- New `migrations/*.sql` files ship in the same PR as the `sqlx::query!` changes that depend on them; commit the updated `.sqlx/` cache alongside.
49- Require `cargo test`, `cargo clippy -- -D warnings`, and `cargo fmt --check` green before merge.
50- Squash-merge; never merge with `unwrap()`/`panic!()` introduced in a route handler — `?` and `AppError` only.

Works with Cursor · Claude Code · Windsurf · AGY

Architecture notes

Architecture Overview

High-throughput systems architecture using Rust, Axum, Tokio, and sqlx with compile-time verified database operations.

Key Advantages

  • Zero Memory Leaks & Zero GC Pauses: Memory safety guaranteed at compile time without garbage collector spikes.
  • Compile-Time Query Verification: sqlx checks SQL queries against PostgreSQL schemas during cargo build, preventing runtime syntax errors.
  • Massive Concurrency: Tokio lightweight asynchronous tasks handle tens of thousands of concurrent connections on minimal hardware.

Building pages rather than an API? See Axum + htmx + Askama for server-rendered HTML, Leptos full-stack for a Rust UI, Loco for Rails-style conventions and Tauri + React for desktop apps. What open-source Rust projects actually use: Building web apps in Rust in 2026.

Frequently asked questions

Does this AGENTS.md work with Cursor, Claude Code, and Windsurf?

Yes — AGENTS.md is the open, cross-tool standard read by Cursor, Claude Code, Windsurf, and 30+ other agents. A dedicated .mdc file is also included for Cursor's native .cursor/rules format.

Why does sqlx need a .sqlx/ offline query cache?

sqlx's query_as! and query! macros connect to a live database at compile time to verify SQL syntax and types, which breaks CI environments that don't have database access. cargo sqlx prepare snapshots that verification into a .sqlx/ cache file that ships in the repo, so cargo build can compile-check queries offline using the cached metadata instead of a live connection.

Why is unwrap() banned in route handlers?

unwrap() panics the current thread on an Err or None, and in an Axum handler that means the request-handling task crashes — potentially with a raw error message leaking to the client and no structured logging around the failure. Propagating errors with the ? operator into a typed AppError that implements IntoResponse guarantees every failure path returns a consistent, controlled HTTP response instead.

Why does Rust work well with AI coding agents?

Most mistakes an agent makes in Rust surface as compiler errors with a line number and a suggested fix: type mismatches, unhandled enum variants, borrow conflicts and, with sqlx, SQL that does not match the schema. A loop of cargo check, cargo clippy -- -D warnings and cargo test after every change lets the agent correct itself before anything runs.

Is this rule for an API or a full web app?

It targets JSON APIs and backend services with a separate frontend. For server-rendered pages use the Axum + htmx + Askama rule, for a full-stack Rust UI the Leptos rule, and for Rails-style conventions the Loco rule.

Used in production

Explore all stacks
Where this stack fits

Stack It First recommends it at:

Walk the backend roadmap