Axum REST API Project Structure
Full REST API with SQLx, tower middleware, and structured handlers.
Project Directory
myproject/
Cargo.toml
Cargo.lock
.gitignore
README.md
.env
DB connection, secrets
.env.example
sqlx-data.json
Offline query checking
migrations/
SQLx migrations
001_create_users.sql
src/
main.rs
Bootstrap and server
lib.rs
Library root for testing
config/
mod.rs
settings.rs
Typed config with envy
routes/
Route modules
mod.rs
Combines all routers
users.rs
health.rs
handlers/
Request handlers
mod.rs
users.rs
health.rs
models/
Domain types
mod.rs
user.rs
db/
Database layer
mod.rs
pool.rs
Connection pool setup
users.rs
User queries
middleware/
mod.rs
auth.rs
JWT or session auth
logging.rs
Request logging
error/
mod.rs
AppError and responses
extractors/
Custom extractors
mod.rs
json.rs
Validated JSON extractor
Why This Structure?
This structure separates HTTP concerns from database access using Axum's extractor pattern. SQLx provides compile-time checked queries, while tower middleware handles cross-cutting concerns. Each layer has a clear responsibility—routes define paths, handlers process requests, db executes queries.
Key Directories
- src/routes/-Route definitions, maps paths to handlers
- src/handlers/-Request processing, calls db layer
- src/db/-SQLx queries and connection pool
- src/middleware/-Tower layers for auth, logging, CORS
- src/extractors/-Custom request extractors with validation
Handler with State
// src/handlers/users.rs
use axum::{extract::State, Json};
use crate::{db, error::AppError, models::User};
pub async fn list_users(
State(pool): State,
) -> Result>, AppError> {
let users = db::users::find_all(&pool).await?;
Ok(Json(users))
}
Getting Started
cargo new myproject && cd myproject- Add
axum,sqlx,tokio,tower-httptoCargo.toml sqlx database create && sqlx migrate runcargo run
When To Use This
- Building REST APIs with database access
- Need compile-time query verification
- Authentication and middleware requirements
- APIs with 10+ endpoints
- Team projects with clear layer separation
Trade-offs
- More boilerplate-Rust requires explicit error handling
- Build times-SQLx compile-time checks add to build time
- Module ceremony-mod.rs files needed for each folder
Naming Conventions
- Modules-lowercase, singular (handler, model, db)
- Files-Lowercase with underscores (user_handler.rs)
- Types-PascalCase (User, CreateUserRequest)
- Functions-snake_case (find_by_id, create_user)