3.1.6 Board API routes — `GET` projection, `GET` column page, `POST` move (thin, workspace-gated)
Estimate: 16m · Depends on: 3.1.4, 3.1.5
The HTTP surface for the board backend — thin route handlers (parse, session, ONE service call, map typed errors), no business logic, no Prisma (CLAUDE.md). Each carries an explicit workspaceId application-layer gate (finding #26).
Routes (under the project key, mirroring the existing /api/projects/[key]/… tree):
GET /api/projects/[key]/board→BoardProjectionDto(the default board projection — columns, first page of cards per column with counts + cursors,unmappedStatuses). CallsboardsService.getBoard.GET /api/projects/[key]/board/columns/[columnId]/cards?cursor=&limit=→PagedColumnCardsDto(the lazy "load more" page for one column). CallsboardsService.loadColumnCards.POST /api/projects/[key]/board/movewith body{ workItemId, toColumnId, beforeId?, afterId? }→ the movedBoardCardDto. CallsboardsService.moveCard. MapsIllegalBoardMoveError→ 409,UnmappedColumnTargetError→ 422, not-found → 404, unauthenticated → 401 — the status codes the 3.2 UI branches on (409 ⇒ snap back).
Resolve the project by key + session workspace; the board id is implicit (the project’s default board) for v1, but the service takes boardId so multi-board routing is a later, non-breaking addition. Validate the request body shape; reject unknown columns / cross- project ids before the service call.
Acceptance criteria
- Three route handlers, each parsing the request, reading the session via
getSession(), gating onworkspaceId, calling exactly one service method, and returningNextResponse.json. - No
db.*/prisma.$transactionin any route file; typed service errors are mapped to 401 / 404 / 409 / 422 (an illegal move → 409, the snapback signal). GET …/boardreturns the projection;GET …/columns/[id]/cardsreturns the next page by cursor;POST …/movereturns the moved card or the right typed error.- Smoke/route tests assert the happy path + the 409 illegal-move mapping + the 401 unauthenticated guard.
Context refs
app/api/projects/[key]/…— the existing project-scoped route tree + key/workspace resolution patternlib/services/boardsService.ts—getBoard/loadColumnCards/moveCard(3.1.4, 3.1.5)lib/boards/errors.ts— the typed errors the route maps to status codesmotir-core/CLAUDE.md— Route layer rules (HTTP only, one service call, error→status mapping); finding #26 — explicit workspace gate