axum-api-template
An opinionated Axum web API template modelled after the ASP.NET project layout.
Philosophy
| ASP.NET concept |
This template |
Program.cs bootstrap |
src/main.rs |
appsettings.json + env overrides |
config/*.toml + APP__* env vars |
IServiceCollection / DI |
AppState (shared via Axum State extractor) |
| Controller classes |
src/handlers/ — one file per resource |
| Request / Response DTOs |
src/models/ — *Request / *Response suffix convention |
ProblemDetails |
src/errors/ApiError — single IntoResponse impl |
app.Use* middleware pipeline |
src/routes/mod.rs build_router() |
Route groups (MapGroup) |
src/routes/v1.rs nested routers |
Project layout
axum-api-template/
├── config/
│ ├── default.toml # base config (committed)
│ ├── development.toml # dev overrides (committed)
│ └── production.toml # prod overrides (committed; no secrets)
├── src/
│ ├── main.rs # entry point — startup sequence
│ ├── lib.rs # re-exports all modules for integration tests
│ ├── config/ # AppConfig (layered TOML + env vars)
│ ├── errors/ # ApiError → consistent JSON error envelope
│ ├── handlers/
│ │ ├── health.rs # GET /health/live, /health/ready
│ │ └── items.rs # full CRUD scaffold for an example resource
│ ├── middleware/
│ │ ├── auth.rs # AuthenticatedUser extractor (JWT stub)
│ │ └── request_id.rs # X-Request-ID documentation anchor
│ ├── models/
│ │ ├── item.rs # Item domain struct + CreateItemRequest + ItemResponse
│ │ └── pagination.rs # PaginationQuery + PagedResponse<T>
│ ├── routes/
│ │ ├── mod.rs # build_router() — middleware stack assembly
│ │ └── v1.rs # /api/v1/* route table
│ └── state/ # AppState (Arc<InnerState>)
├── tests/
│ ├── common/mod.rs # shared test helpers (TestServer factory)
│ └── health_test.rs # integration tests for /health/*
├── .env.example
├── .gitignore
└── Cargo.toml
Quickstart
cp .env.example .env
# Edit .env — at minimum set APP__DATABASE__URL
cargo run
# → Listening on 0.0.0.0:8080
Configuration
Configuration is resolved in order of increasing precedence:
config/default.toml
config/{APP_ENV}.toml (set APP_ENV=production in your environment)
- Environment variables prefixed
APP__ with double-underscore separators
# Examples
APP__SERVER__PORT=9000
APP__DATABASE__URL=postgres://user:pass@db/myapp
APP__AUTH__JWT_SECRET=super-secret
Naming conventions
| Thing |
Convention |
Example |
| Handler functions |
verb_resource |
list_items, create_item |
| Request DTOs |
{Resource}CreateRequest |
CreateItemRequest |
| Response DTOs |
{Resource}Response |
ItemResponse |
| Route modules |
version-namespaced |
routes/v1.rs |
| Config env vars |
APP__{SECTION}__{KEY} |
APP__SERVER__PORT |
| Error variants |
PascalCase domain names |
ApiError::NotFound |
Endpoints
| Method |
Path |
Description |
GET |
/health/live |
Liveness check |
GET |
/health/ready |
Readiness check (dependency health) |
GET |
/api/v1/items |
List items (paginated) |
POST |
/api/v1/items |
Create item |
GET |
/api/v1/items/:id |
Get item by ID |
PUT |
/api/v1/items/:id |
Update item |
DELETE |
/api/v1/items/:id |
Delete item |
Error shape
All errors return a consistent JSON envelope:
{
"error": {
"status": 404,
"code": "NOT_FOUND",
"message": "Item 00000000-0000-0000-0000-000000000001 not found",
"trace_id": "01924b72-1234-7abc-def0-000000000000"
}
}
Next steps