Skip to content

feat: semesters list end to end - #13

Open
AskingRouge wants to merge 2 commits into
mainfrom
nikhil/semesters-list
Open

AskingRouge wants to merge 2 commits into
mainfrom
nikhil/semesters-list

Conversation

@AskingRouge

Copy link
Copy Markdown
Collaborator

Ticket

Closes #6

What changed

  • GET /api/v1/semesters returns { items, total }, newest first (by start date, name as tiebreak).
  • Response Zod schema in packages/shared/src/semesters.ts.
  • Query in apps/api/src/services/semesters.ts; route only handles request/response, with a // TODO(auth).
  • Route test (apps/api/tests/semesters.test.ts) with the service stubbed, so it runs without a database like CI.
  • /semesters page fetches through the typed client, with loading, error, and empty states.

Notes:

  • The service imports db lazily because src/db/index.ts throws on import without DATABASE_URL, which would break every API test in CI. db/index.ts itself is unchanged.
  • Dates are formatted in UTC so "2026-08-17" doesn't display as Aug 16 in Eastern time.
  • No pagination. I couldn't open the TDD's Lists convention in Notion, so please check that { items, total } without paging matches it.
  • This conflicts trivially with Sean/api errors validation #8 (app.ts) and FormRenderer, answer validation, and testing router validation  #10 (shared/src/index.ts). Whichever merges second keeps both lines.

Why

First end-to-end feature (DB → service → route → shared schema → typed client → page) to set the pattern for later features.

How to verify

  1. bun run db:migrate, then add a few semesters in bun run db:studio (mark one current).
  2. bun run dev, then open http://localhost:3000/semesters: newest first, correct dates, "Current" marker.
  3. Delete all semesters to see the empty state; stop the API to see the error state.
  4. bun run test:all passes without DATABASE_URL.

Checklist

  • CI passes locally (format, lint, typecheck, tests, build)
  • TDD updated if behavior changed (no Notion access; lead please remove ‼️ for semesters list)
  • No secrets or real member data in the diff

@VidurShah VidurShah left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work Nikhil. The layering is exactly what the ticket asked for (service, thin route, shared schema, typed client). The // TODO(auth), the lazy db import so CI runs without a database, and formatting dates in UTC are all well thought out.

Please fix

  • Merge conflict. #8 has merged and this now conflicts in apps/api/src/app.ts. Rebase on main and keep both the error handlers and the /semesters route.
  • Pagination. The Lists convention in TDD 3.2 is ?page=1&pageSize=50 (max 200), returning { items, total }, where total is the full count, not the page length. Validate the query with validate("query", ...) from #8, so a bad ?pageSize comes back as a standard VALIDATION error. (You mentioned you couldn't open the TDD in Notion. I'll sort out your access.)
  • The term enum is defined twice. It's in SEMESTER_TERMS in packages/shared and again in the pgEnum in db/schema/semesters.ts. Have the schema use the shared one, pgEnum("semester_term", SEMESTER_TERMS), so they can't drift apart.
  • The sort order isn't tested. The route test stubs the service, so the newest-first ordering and the name tiebreak never actually run. A good fix: pull the ordering into something you can test without the database, or add a service test that runs when DATABASE_URL is set.

Small

  • The error handling already uses #8's format: an unhandled database failure comes back as {"error":{"code":"INTERNAL",...}} with a 500. The page could show the API's error message instead of the generic text, now that responses carry one.

Re-request review when it's ready.

GET /api/v1/semesters returns { items, total }, newest first, with the
response schema in packages/shared, the query in a service, a route test,
and a /semesters page with loading, error, and empty states.
- Paginate per the TDD 3.2 Lists convention (?page, ?pageSize max 200),
  validated with validate("query", ...); total is the full count
- Shared pageQuerySchema in packages/shared for later list endpoints
- semester_term pgEnum uses SEMESTER_TERMS from packages/shared
- Service test against the database (skipped without DATABASE_URL)
  covering newest-first order, the name tiebreak, paging, and total
- Page shows the API's error message when there is one
@AskingRouge
AskingRouge force-pushed the nikhil/semesters-list branch from 8c250f9 to e459da5 Compare October 28, 2026 02:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Semesters list, end to end

2 participants