Files
bible/rules/patterns/go-api/contract.md
T
Mikhail ChusavitinandClaude Opus 5 59e3107197 Add timeout, busy-state, parser-variant and build-freshness rules
- task-discipline: require explicit target platform/language before implementation
- go-api: bounded context deadline on every outbound network/DB call
- controls-selection: visible busy state for any control triggering such a call
- import-export: enumerate real format variants before writing a parser
- app-binary: embedded assets do not hot-reload; verify rebuild before diagnosing

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 21:00:42 +03:00

97 lines
3.3 KiB
Markdown

# Contract: REST API Conventions (Go Web Applications)
Version: 1.2
## 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`.
## Timeouts
- Every outbound network or database call must run under a bounded `context.Context` deadline.
No call reachable from a user action may block indefinitely.
- The deadline is set by the handler or service that owns the request, not by the caller of the app.
- On timeout, return a real error status (`504` for an upstream timeout, `500` for an internal one)
with a human-readable message. Never let a timed-out call fall through as an empty success.
- The UI side of the same rule — visible busy state for the duration of the call — is owned by
`controls-selection`.
## 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
Long-running operations return `202 {"task_id": "..."}` immediately and the client polls
`GET /api/tasks/:id`. Full flow, statuses, and task struct: `go-background-tasks` contract.