Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

12 — API Mapping (Timetable Module)

Exact wire contract per 00-shared/07. Base /api/v1 (main.ts:44-48); envelope {success,message,data,meta?,timestamp,requestId}. Global guards: RateLimitGuard → JwtAuthGuard → RbacGuard (app.module.ts:129-131); no timetable endpoint carries RBAC metadata (rbac.guard.ts:29 → JWT-only, 01 §5). Bearer JWT; tenantId from token only (base.repository.ts:33-35).


0. Module-wide request envelope & client policy

AspectContract
HeadersAuthorization: Bearer; x-request-id client UUID; Content-Type: application/json
Tenancynever in body; server injects tenantId + isDeleted:false scope (base.repository.ts:20-30)
Cachingcatalogs (classes, subjects, teachers, rooms, years) 24 h TTL; timetable grids 5 min TTL (00-shared/06 §3.3)
Offlinereads last-good cache + banner; writes blocked (no offline queue)
Retrybackoff on 5xx/network; no auto-retry on 429
Idempotencycreate is not idempotent — no duplicate guard on identical slots (class-level dupes legal, OQ-1); Idempotency-Key optional (B6 shared ledger)

1. Create — the only write

POST /timetable — create entry (S4)

  • Body: CreateTimetableEntryDto (create-timetable-entry.dto.ts:5-38, F1): exactly classId, subjectId, teacherId, roomId?, dayOfWeek (enum Mon–Sat, timetable.schema.ts:7-14), startTime, endTime, academicYearId. Unknown keys rejected (whitelist + forbidNonWhitelisted, main.ts:50-57).
  • 200 data: TimetableEntry (raw doc, refs as ObjectIds; timestamps auto, timetable.schema.ts:16).
  • 409 ConflictException "Schedule conflict detected" (timetable.service.ts:28): same-day existing entry shares teacherId or roomId with overlapping [startTime,endTime) (timetable.service.ts:17-22,62-69). No structured error code — client matches status.
  • 400 validation (invalid ids, sunday dayOfWeek, unknown keys).
  • Side-effects: TimetableEntryCreated emitted (timetable.service.ts:33-44, payload {entryId, classId, teacherId}) — no queue routing found (01 §6).

2. Reads — two scopes, no pagination

GET /timetable?classId=<id> — class grid (S1)

  • classId takes precedence when both params present (timetable.controller.ts:26-27).
  • Success: data: TimetableEntry[]non-paginated array, no meta.
  • Sort: server-side {dayOfWeek:1, startTime:1} (timetable.service.ts:48-53).

GET /timetable?teacherId=<id> — teacher grid (S2)

  • Same shape; sort {dayOfWeek:1, startTime:1} (timetable.service.ts:55-60).

GET /timetable — bare (no params)

  • Returns [] (timetable.controller.ts:28) — no "all entries" fetch; client always scopes (OQ-4).

Missing surfaces (derived)

  • No ?roomId= filter (timetable.controller.ts:22-25) — room view is a client composition (03 §J5); native filter (planned).
  • No :id routes, no PATCH, no DELETE (timetable.controller.ts:10) — update/delete (planned) (IMPLEMENTATION_PLAN.md:226).
  • No pagination, filters, or date params — grid pages are the whole week by model (weekday + time only, timetable.schema.ts:30-37).

3. Supporting catalogs (read-only for this module)

EndpointUsed bySource
GET /classes (+ by-year/:academicYearId)S1 class picker, F1 classIdclass.controller.ts:27-33
GET /subjects (paginated)F1 subject picker, slot labelssubject.controller.ts:27-29
GET /teachers (paginated)F1 teacher picker, slot labelsteacher.controller.ts:27-29
GET /rooms (paginated, ?page=&limit=)F1 room picker, room labelsrooms.controller.ts:30-34
GET /academic-yearsF1 year picker (isCurrent)academic-year.controller.ts:27-30
GET /dashboard/overviewtimetable-related KPIs (proposed)dashboard module

4. Loading / streaming / realtime

ScreenLoadingRealtime
S1/S2/S3 gridskeleton grid— (timetable WS events (planned); TimetableEntryCreated unrouted today, 01 §6)
S4 editorsubmit spinner only
S5 bannerappears on 409 / pre-flight

5. Client error mapping table (module)

ScreenCodeUI
all401silent refresh; fail → sessionExpired
all403403 screen (future server enforcement; client-side perm gate today)
grid404 (catalog ref gone)slot shows "—"
S4409ConflictBanner, keep form
S4400per-field errors
all429"Try again in a moment", no auto-retry
all5xxgeneric + requestId + retry

6. Pagination summary

  • GET /timetable: non-paginated array — one request = whole weekly grid per scope. Large-school caveat: verify payload size; server pagination (planned) (IMPLEMENTATION_PLAN.md:226).
  • Catalogs paginate via PaginationQueryDto — client loads with limit=100 and caches 24 h.

7. Optimistic / undo

  • Grid refresh + editor create: server-first (read-after-write; insert slot only after 200).
  • No destructive op exists today; delete (planned) will follow 00-shared/06 §3.5 (confirm dialog; soft-delete via BaseRepository pattern — base.repository.ts:68-74).
  • Drag & drop opens the editor (create semantics) — never an optimistic move (09 §5).