Backend changes
Conventions for API routes, domain code, jobs and errors in msaving/backend.
Where code goes
| Layer | Folder | Holds |
|---|---|---|
| core | app/core/ |
Settings, sign-in building blocks, session check, per-request database session, account event log, update check |
| api | app/api/ |
FastAPI app (web app files, CSP, Origin check), dependencies, error mapping, models, routes/v1/ |
| domain | app/domain/ |
Logic used by routes: transactions, dashboard, export, starter categories |
| jobs | app/jobs/ |
Migrations runner, scheduler, daily jobs (python -m app.jobs.<name>) |
app/launch.py, app/admin.py |
The container’s start; operator commands |
A route should be thin: parse, call domain code or SQL, commit, return.
Route conventions
- Under
/v1. Additive changes only within a version (new fields and routes are fine; removing or renaming isn’t): phones in the stores talk to older and newer servers. - Writes call
db.commit()in the route before returning. - Optimistic concurrency: editable rows take the
versionthe client loaded; mismatch →409 conflict. - Idempotent creates: the client makes the id; saving the same id twice does nothing.
- Never
INSERT ... RETURNINGorON CONFLICTonbooks: both apply the read policy before the creator is a member. - Errors: return
{"detail": "<code>"}. Database errors start with a code (owner_required: ...) andapp/api/errors.pymaps them to a status. A new code needs translations inweb/src/i18n/vi.jsonanden.json. See Error codes. - Money:
intminor units in Pydantic models. Neverfloat. - Timezones: use the
Timezonetype (app/api/models.py); it maps aliases to names the database knows. - Settings: optional, with safe defaults, added to
.env.example, both READMEs and Environment variables. - Admin-only routes go in
routes/v1/admin.pyand call a database function that checksrequire_admin().
Checklist for a new route
- Route in
app/api/routes/v1/<area>.py, registered with the/v1router. - Request/response models (ints for money, UUIDs for ids).
- Tests in
tests/, including another user getting404/403(RLS). - A paired PR to the docs (
src/content/docs/in msaving-landing): the route on API routes, and the pages it changes. - If the web app uses it: types in
web/src/lib/api.ts, hooks inlib/queries.ts, in the same PR.
Don’t
- Don’t store balances. They’re sums of entries.
- Don’t give the API the owner’s login.
- Don’t log tokens, codes, passwords or transaction contents.