An educational Axum todo service that demonstrates how to build, test, and observe a small Rust HTTP API. Everything runs entirely in memory, so you can clone the repo and start experimenting immediately—no external services needed.
- Axum 0.7 router with typed request/response handling and middleware (trace, CORS, compression).
- In-memory repository guarded by
tokio::sync::Mutex, exposed through aTodoRepotrait so you can swap in a database later. - Centralized error handling that maps domain errors to consistent JSON bodies.
- Integration-style test (
tests/todos.rs) that exercises the full router without binding a TCP port. - Structured logging with
tracingandRUST_LOG/EnvFiltersupport.
- Rust toolchain (1.74+ recommended) with
cargo pkg-config/OpenSSL are not required because storage is in-memory
Everything you need is already captured in Cargo.toml, so a regular cargo
build will fetch crates automatically:
cargo checkcargo runThe server listens on 0.0.0.0:8080 by default. Locally, you can opt into more
verbose logs by exporting RUST_LOG/RUST_API_LOG=debug. The binary calls
dotenvy::dotenv(), so placing secrets or overrides inside a .env file keeps
them out of your shell history.
# health check
curl -i http://localhost:8080/health
# create a todo
curl -i -X POST http://localhost:8080/todos \
-H 'content-type: application/json' \
-d '{ "title": "learn rust" }'
# toggle completion
curl -i -X PUT http://localhost:8080/todos/1 \
-H 'content-type: application/json' \
-d '{ "done": true }'{
"id": 1,
"title": "learn rust",
"done": false
}| Method | Path | Description | Success codes | Request body |
|---|---|---|---|---|
| GET | /health |
Liveness probe | 200 | None |
| GET | /todos |
List every todo | 200 | None |
| POST | /todos |
Create a todo | 201 | { "title": "..." } |
| GET | /todos/:id |
Fetch a todo | 200 | None |
| PUT | /todos/:id |
Update title and/or completion flag | 200 | { "title": "...?", "done": true? } |
| DELETE | /todos/:id |
Remove a todo | 204 | None |
- Titles are trimmed and cannot be empty.
PUTrequests must include at least one field.- Missing records respond with
404 {"error":"not found"}. - Validation issues respond with
400 {"error":"validation error: ..."}. - Unexpected failures respond with
500 {"error":"internal error"}.
Run the full suite, including the router-level CRUD flow, with:
cargo testThe integration test (tests/todos.rs) exercises create → read → update →
list → delete. Use it as a template when adding new routes or when swapping
the repository implementation.
- Replace the
InMemoryrepo instate.rswith a database-backed struct that still implementsTodoRepo. - Add authentication/authorization layers via Axum middleware.
- Ship richer telemetry by forwarding
tracingspans to OpenTelemetry. - Wrap the server with Docker and deploy it wherever
cargobinaries run.