|
123456789101112131415161718192021222324252627282930 |
- # Architecture
-
- ```
- browser → Vite :5173 --proxy /api,/health--> Axum :8080 → PostgreSQL
- ↑ │
- └── generated client ← openapi.json ← utoipa-axum router
- ```
-
- ## Why this shape
-
- One workspace. The server owns HTTP and persistence. The web app owns interaction. The contract package is generated, never edited.
-
- Config is shared on purpose. `config/default.toml` is read by both `AppConfig::load` and `apps/web/vite.config.ts`, so the reverse proxy target cannot drift from the bind address.
-
- ## Request path
-
- 1. Vite binds `127.0.0.1` (not `localhost`) so the browser never hits IPv6 `::1`.
- 2. Browser calls `/api/v1/items`.
- 3. Vite proxies to `http://127.0.0.1:8080`.
- 4. `SetRequestIdLayer` assigns `X-Request-Id`.
- 5. `request_logging` records method, path, status, latency, request id.
- 6. Handler returns `Result<T, ApiError>`. Errors become a single JSON envelope.
-
- ## Adding an endpoint
-
- 1. DTO in `apps/server/src/models`.
- 2. Handler in `apps/server/src/handlers` with `#[utoipa::path]`.
- 3. Register it with `.routes(routes!(your_handler))` in `routes/mod.rs`.
- 4. `just generate-api`.
- 5. Call the new function from `apps/web/src/api/generated.ts`.
|