Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Work items

MOTIR-162Done

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]/boardBoardProjectionDto (the default board projection — columns, first page of cards per column with counts + cursors, unmappedStatuses). Calls boardsService.getBoard.
  • GET /api/projects/[key]/board/columns/[columnId]/cards?cursor=&limit=PagedColumnCardsDto (the lazy "load more" page for one column). Calls boardsService.loadColumnCards.
  • POST /api/projects/[key]/board/move with body { workItemId, toColumnId, beforeId?, afterId? } → the moved BoardCardDto. Calls boardsService.moveCard. Maps IllegalBoardMoveError409, UnmappedColumnTargetError422, 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 on workspaceId, calling exactly one service method, and returning NextResponse.json.
  • No db.* / prisma.$transaction in any route file; typed service errors are mapped to 401 / 404 / 409 / 422 (an illegal move → 409, the snapback signal).
  • GET …/board returns the projection; GET …/columns/[id]/cards returns the next page by cursor; POST …/move returns 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 pattern
  • lib/services/boardsService.tsgetBoard / loadColumnCards / moveCard (3.1.4, 3.1.5)
  • lib/boards/errors.ts — the typed errors the route maps to status codes
  • motir-core/CLAUDE.md — Route layer rules (HTTP only, one service call, error→status mapping); finding #26 — explicit workspace gate