# Contract: REST API Conventions (Go Web Applications) Version: 1.0 ## URL Naming - Resources are plural nouns: `/api/assets`, `/api/components`, `/api/pricelists`. - Nested resources use parent path: `/api/assets/:id/components`. - Actions that are not CRUD use a verb suffix: `/api/pricelists/:id/export-csv`, `/api/tasks/:id/cancel`, `/api/sync/push`. - No verbs in resource paths: use `/api/assets` + DELETE, not `/api/delete-asset`. ## HTTP Methods and Status Codes | Operation | Method | Success | Notes | |-----------|--------|---------|-------| | List | GET | 200 | | | Get one | GET | 200 / 404 | | | Create | POST | 201 | Return created resource | | Update | PUT / PATCH | 200 | Return updated resource | | Delete / Archive | DELETE | 200 or 204 | | | Async action | POST | 202 | Return `{task_id}` | | Validation error | POST/PUT | 422 | Return field errors | | Server error | any | 500 | Log full error server-side | | Not found | any | 404 | | | Unauthorized | any | 401 | | - Never return `200 OK` for validation errors — use `422 Unprocessable Entity`. - Never return `200 OK` with `{"error": "..."}` in the body — use the correct status code. ## Error Response Format All non-2xx responses return a consistent JSON body: ```json { "error": "human-readable message", "fields": { "serial_number": "Serial number is required", "price": "Must be greater than 0" } } ``` - `error` — always present, describes what went wrong. - `fields` — optional, present only for validation errors (422). Keys match form field names. - Never expose raw Go error strings, stack traces, or SQL errors to the client. ## List Response Format ```json { "items": [...], "total_count": 342, "page": 2, "per_page": 50, "total_pages": 7 } ``` - Always include pagination metadata even if the client did not request it. - `items` is always an array — never `null`, use `[]` for empty. ## Request Conventions - Accept `application/json` for API endpoints. - HTML form submissions (htmx) use `application/x-www-form-urlencoded` or `multipart/form-data`. - File uploads use `multipart/form-data`. - Query parameters for filtering and pagination: `?page=1&per_page=50&search=abc&status=active`. ## Health and Utility Endpoints Every application must expose: ``` GET /health → 200 {"status": "ok"} GET /api/db-status → 200 {"ok": true} or 500 {"ok": false, "error": "..."} ``` - `/health` must respond even if DB is down (for load balancer probes). - `/api/db-status` checks the actual DB connection and returns its state. ## Async Actions For long-running operations return immediately with a task reference: ``` POST /api/pricelists/create → 202 {"task_id": "abc123"} GET /api/tasks/abc123 → 200 {"status": "running", "progress": 42, "message": "Processing..."} GET /api/tasks/abc123 → 200 {"status": "success", "result": {...}} ``` See `go-background-tasks/contract.md` for full task contract.