|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- # 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
-
- ```bash
- 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:
-
- 1. `config/default.toml`
- 2. `config/{APP_ENV}.toml` (set `APP_ENV=production` in your environment)
- 3. Environment variables prefixed `APP__` with double-underscore separators
-
- ```bash
- # 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:
-
- ```json
- {
- "error": {
- "status": 404,
- "code": "NOT_FOUND",
- "message": "Item 00000000-0000-0000-0000-000000000001 not found",
- "trace_id": "01924b72-1234-7abc-def0-000000000000"
- }
- }
- ```
-
- ## Next steps
-
- - [ ] Replace the in-memory stubs in `handlers/items.rs` with real `sqlx` queries
- - [ ] Implement JWT validation in `middleware/auth.rs`
- - [ ] Add `APP__DATABASE__URL` to `.env` and uncomment the DB pool in `state/mod.rs`
- - [ ] Tighten the CORS policy in `routes/mod.rs` for production
- - [ ] Add a service layer between handlers and DB for business logic
|