No canonical snake_case/camelCase serialization policy
This commit is contained in:
@@ -465,18 +465,14 @@ class BansByCountryResponse(BaseModel):
|
||||
|
||||
## 5. Pydantic Models
|
||||
|
||||
- Every model inherits from `pydantic.BaseModel`.
|
||||
- Use `model_config = ConfigDict(strict=True)` where appropriate.
|
||||
- Field names use **snake_case** in Python, export as **camelCase** to the frontend via alias generators if needed.
|
||||
- Validate at the boundary — once data enters a Pydantic model it is trusted.
|
||||
- Use `Field(...)` with descriptions for every field to keep auto-generated docs useful.
|
||||
- Separate **request models**, **response models**, and **domain (internal) models** — do not reuse one model for all three.
|
||||
### Base Class
|
||||
|
||||
Every model in `app/models/` **must** inherit from `BanGuiBaseModel` (defined in `app/models/response.py`), not from `pydantic.BaseModel` directly.
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
from datetime import datetime
|
||||
from app.models.response import BanGuiBaseModel
|
||||
|
||||
class BanResponse(BaseModel):
|
||||
class BanResponse(BanGuiBaseModel):
|
||||
ip: str = Field(..., description="Banned IP address")
|
||||
jail: str = Field(..., description="Jail that issued the ban")
|
||||
banned_at: datetime = Field(..., description="UTC timestamp of the ban")
|
||||
@@ -484,6 +480,24 @@ class BanResponse(BaseModel):
|
||||
ban_count: int = Field(..., ge=1, description="Number of times this IP was banned")
|
||||
```
|
||||
|
||||
`BanGuiBaseModel` sets `strict=True` and documents the naming policy. Do **not** override `model_config` on individual models unless you have a specific, documented reason.
|
||||
|
||||
### API Field Naming Policy — snake_case everywhere
|
||||
|
||||
All API field names use **`snake_case`** in Python, in the JSON wire format, and in the corresponding TypeScript interfaces. There is no `alias_generator` that converts to camelCase.
|
||||
|
||||
- ✅ Python field: `active_jails` → JSON key: `"active_jails"` → TypeScript property: `active_jails`
|
||||
- ❌ Do **not** add a camelCase `alias_generator` to individual models.
|
||||
- ❌ Do **not** mix field name conventions within a single API response.
|
||||
|
||||
This policy eliminates a whole class of frontend–backend contract bugs. If the naming policy ever needs to change (e.g. to emit camelCase), change `BanGuiBaseModel` once — all models update automatically.
|
||||
|
||||
### Other Model Rules
|
||||
|
||||
- Validate at the boundary — once data enters a Pydantic model it is trusted.
|
||||
- Use `Field(...)` with descriptions for every field to keep auto-generated docs useful.
|
||||
- Separate **request models**, **response models**, and **domain (internal) models** — do not reuse one model for all three.
|
||||
|
||||
### Using `Literal` Types for Constrained Strings
|
||||
|
||||
When a field should only accept a small set of predefined values, use `Literal` to enforce this at the type level:
|
||||
|
||||
@@ -1,24 +1,3 @@
|
||||
## 24) API response wrapper shape is inconsistent
|
||||
- Where found:
|
||||
- [backend/app/routers/dashboard.py](backend/app/routers/dashboard.py)
|
||||
- [backend/app/routers/jails.py](backend/app/routers/jails.py)
|
||||
- [frontend/src/types](frontend/src/types)
|
||||
- Why this is needed:
|
||||
- Inconsistent payload envelopes increase frontend branching and integration bugs.
|
||||
- Goal:
|
||||
- Define and enforce a consistent response envelope policy.
|
||||
- What to do:
|
||||
- Standardize endpoint response forms.
|
||||
- Align frontend typing and parsing strategy.
|
||||
- Possible traps and issues:
|
||||
- Breaking contract for existing clients.
|
||||
- Docs changes needed:
|
||||
- Add API response style guide.
|
||||
- Doc references:
|
||||
- [Docs/Backend-Development.md](Docs/Backend-Development.md)
|
||||
|
||||
---
|
||||
|
||||
## 25) No canonical snake_case/camelCase serialization policy
|
||||
- Where found:
|
||||
- [backend/app/models/server.py](backend/app/models/server.py)
|
||||
|
||||
@@ -966,6 +966,15 @@ This pattern prevents **stale session flicker** — the brief moment when a user
|
||||
| Directories | lowercase kebab‑case or camelCase | `components/`, `hooks/` |
|
||||
| Boolean props/variables | `is`/`has`/`should` prefix | `isLoading`, `hasError` |
|
||||
|
||||
### API Field Names — snake_case
|
||||
|
||||
All TypeScript interface properties that mirror backend API responses use **`snake_case`**, not `camelCase`. This matches the JSON wire format emitted by the backend without any transformation layer.
|
||||
|
||||
- ✅ `active_jails`, `total_bans`, `log_level`, `db_purge_age`
|
||||
- ❌ `activeJails`, `totalBans`, `logLevel`, `dbPurgeAge` (do **not** use camelCase for API shapes)
|
||||
|
||||
This applies to all interfaces in `src/types/`. Internal component state, props, and hook return values use `camelCase` as normal TypeScript convention.
|
||||
|
||||
---
|
||||
|
||||
## 9. Linting & Formatting
|
||||
|
||||
Reference in New Issue
Block a user