diff --git a/docs/CODE_STANDARDS.md b/docs/CODE_STANDARDS.md index 8faef05..0d5d42b 100644 --- a/docs/CODE_STANDARDS.md +++ b/docs/CODE_STANDARDS.md @@ -38,7 +38,10 @@ rolled out. Update this doc when a standard changes. (`ValidationError`, `NotFoundError`, `ConflictError`, …) and let the single terminal error handler format them — do not hand-roll `res.status().json({ error })`. Express 5 forwards rejected async handlers automatically, so route bodies should not wrap everything in - `try/catch`. Clients read `message`/`code` + status. + `try/catch`. Clients read `message`/`code` + status. **Canonical example:** + `routes/categories.cts`. (The terminal handler already normalizes any legacy + `standardizeError`/`{ error }` body to the same shape, so converted and not-yet-converted + routes emit identical responses — convert opportunistically.) - **Success envelope (target):** reads return the bare resource/list; mutations return `{ success: true, … }`. - **Validation (target):** validate inputs with the shared validators (throwing diff --git a/routes/monthly-starting-amounts.cts b/routes/monthly-starting-amounts.cts index 011e47b..193cfa6 100644 --- a/routes/monthly-starting-amounts.cts +++ b/routes/monthly-starting-amounts.cts @@ -6,6 +6,7 @@ const { getDb } = require('../db/database.cts'); const { getCycleRange } = require('../services/statusService.cts'); const { accountingActiveSql } = require('../services/paymentAccountingService.cts'); const { toCents, fromCents } = require('../utils/money.mts'); +const { ValidationError } = require('../utils/apiError.cts'); function parseYearMonth(source) { const now = new Date(); @@ -147,7 +148,7 @@ function buildStartingAmountsResponse(db, userId, year, month) { router.get('/', (req: Req, res: Res) => { const parsed = parseYearMonth(req.query); - if (parsed.error) return res.status(400).json({ error: parsed.error }); + if (parsed.error) throw ValidationError(parsed.error, 'month'); const db = getDb(); res.json(buildStartingAmountsResponse(db, req.user.id, parsed.year, parsed.month)); @@ -155,27 +156,24 @@ router.get('/', (req: Req, res: Res) => { router.put('/', (req: Req, res: Res) => { const parsed = parseYearMonth(req.body || {}); - if (parsed.error) return res.status(400).json({ error: parsed.error }); + if (parsed.error) throw ValidationError(parsed.error, 'month'); const firstAmount = Number(req.body?.first_amount); if (!Number.isFinite(firstAmount) || firstAmount < 0 || firstAmount > 1000000000) { - return res - .status(400) - .json({ error: 'first_amount must be a number between 0 and 1000000000' }); + throw ValidationError('first_amount must be a number between 0 and 1000000000', 'first_amount'); } const fifteenthAmount = Number(req.body?.fifteenth_amount); if (!Number.isFinite(fifteenthAmount) || fifteenthAmount < 0 || fifteenthAmount > 1000000000) { - return res - .status(400) - .json({ error: 'fifteenth_amount must be a number between 0 and 1000000000' }); + throw ValidationError( + 'fifteenth_amount must be a number between 0 and 1000000000', + 'fifteenth_amount', + ); } const otherAmount = Number(req.body?.other_amount); if (!Number.isFinite(otherAmount) || otherAmount < 0 || otherAmount > 1000000000) { - return res - .status(400) - .json({ error: 'other_amount must be a number between 0 and 1000000000' }); + throw ValidationError('other_amount must be a number between 0 and 1000000000', 'other_amount'); } const db = getDb();