Context
The transform "lean board -> API shape" (_id -> id, drop __v) is currently duplicated across three call sites, which is what allowed getBoardsByIds to drift and leak __v (see #469 / #470):
user.js mapLeanBoardIds (login, x2)
board.js getBoardsByIds (inline)
board.js getBoardsSync (inline, minimal { id, lastEdited } projection — intentional)
The hotfix (#470) patched the leak but did not deduplicate the logic. A partial extraction was already prototyped (a normalizeLeanBoards helper in api/helpers/board.js) and stashed to keep the hotfix minimal.
Goal
Have one definition of a board's API shape so the lean path and the Mongoose-document path (toJSON) can't diverge again.
Proposed approach
Extract a single pure function (e.g. toBoardApiShape(obj) / normalizeLeanBoards) into api/helpers/board.js and reuse it from:
getBoardsByIds and the two login paths in user.js.
- The schema's
toJSON transform in models/Board.js, so documents and lean objects share the exact same shaping logic.
Notes / decisions to make during implementation:
getBoardsSync intentionally returns only { id, lastEdited }; decide whether it keeps its explicit inline map (preferred) or reuses the helper.
- If the
toJSON transform drops __v itself, the versionKey: false option becomes redundant (can be kept as belt-and-suspenders).
- Watch the
id virtual: with virtuals: true, ret already carries an id string; ensure the shared function does not double-set or clobber it inconsistently between the document and lean paths.
Notes
Context
The transform "lean board -> API shape" (
_id -> id, drop__v) is currently duplicated across three call sites, which is what allowedgetBoardsByIdsto drift and leak__v(see #469 / #470):user.jsmapLeanBoardIds(login, x2)board.jsgetBoardsByIds(inline)board.jsgetBoardsSync(inline, minimal{ id, lastEdited }projection — intentional)The hotfix (#470) patched the leak but did not deduplicate the logic. A partial extraction was already prototyped (a
normalizeLeanBoardshelper inapi/helpers/board.js) and stashed to keep the hotfix minimal.Goal
Have one definition of a board's API shape so the lean path and the Mongoose-document path (
toJSON) can't diverge again.Proposed approach
Extract a single pure function (e.g.
toBoardApiShape(obj)/normalizeLeanBoards) intoapi/helpers/board.jsand reuse it from:getBoardsByIdsand the two login paths inuser.js.toJSONtransform inmodels/Board.js, so documents and lean objects share the exact same shaping logic.Notes / decisions to make during implementation:
getBoardsSyncintentionally returns only{ id, lastEdited }; decide whether it keeps its explicit inline map (preferred) or reuses the helper.toJSONtransform drops__vitself, theversionKey: falseoption becomes redundant (can be kept as belt-and-suspenders).idvirtual: withvirtuals: true,retalready carries anidstring; ensure the shared function does not double-set or clobber it inconsistently between the document and lean paths.Notes
/board/byidsresponses to lock the shape.