12 — API Mapping (Teachers Module)
- 0. Module-wide request envelope & client policy
- 1. Teachers CRUD — module core
- 2. Subject assignments — teaching matrix (S3/S7)
- 3. Supporting catalogs (read-only for this module)
- 4. Loading / streaming / realtime
- 5. Client error mapping table (module)
- 6. Pagination summary
- 7. Optimistic / undo
Exact wire contract per 00-shared/07. Base
/api/v1; envelope{success,message,data,meta?,timestamp,requestId}. Global guards:RateLimitGuard → JwtAuthGuard → RbacGuard(app.module.ts:129-131); no teachers endpoint carries RBAC metadata (rbac.guard.ts:29→ JWT-only).BearerJWT;tenantIdfrom token only (base.repository.ts:33-35).
0. Module-wide request envelope & client policy
| Aspect | Contract |
|---|---|
| Headers | Authorization: Bearer; x-request-id client UUID; Content-Type: application/json |
| Tenancy | never in body; server injects tenantId + isDeleted:false scope (base.repository.ts:20-30) |
| Caching | reference catalogs (departments, designations, subjects, classes, years) 24 h TTL; teacher lists 5 min TTL (00-shared/06 §3.3) |
| Offline | reads last-good cache + banner; writes blocked (no offline queue for this module) |
| Retry | backoff on 5xx/network; no auto-retry on 429 |
| Idempotency | create is retry-safe via 409 duplicate guards (teacher.service.ts:27-38); Idempotency-Key optional (B6 shared ledger) |
1. Teachers CRUD — module core
GET /teachers — list (S1)
- Query:
page(1-based, default 1),limit(1–100, default 20),sort(-field),q(pagination-query.dto.ts:5-30). - Derived caveat:
TeacherService.findignoressortandq(teacher.service.ts:66-78); no status/department filters exist. - Success:
data: Teacher[],meta: {page,limit,totalItems,totalPages,hasNext,hasPrevious}(buildPaginationMeta,pagination-query.dto.ts:41-55). - Errors: 400 (bad page/limit), 401, 429, 5xx.
GET /teachers/:id — detail (S2)
- Success:
data: Teacher(raw schema doc, refs as ObjectIds). - 404
RESOURCE_NOT_FOUND"Teacher not found." (teacher.service.ts:54-58).
POST /teachers — create (S5)
- Body:
CreateTeacherDto(create-teacher.dto.ts:4-52, F1). - 201→200
data: Teacher. 409DUPLICATE_RESOURCE(user profile exists,teacher.service.ts:27-31; employeeNumber exists,:32-38). 400 validation. - Side-effects:
TeacherCreated→in-appjobteacher-created(event-queue-map.ts:31) → notification (inapp.worker.ts:46-53); search index (search-indexer.service.ts:10).
PATCH /teachers/:id — update (S6)
- Body:
UpdateTeacherDto(update-teacher.dto.ts:4-53, F2);$setmerge (teacher.service.ts:80-93);userIdimmutable. - 200
data: Teacher; 404 (missing or soft-deleted); 400. - Side-effects:
TeacherUpdated→audit-writelog-teacher-updated(event-queue-map.ts:32); search re-index (search-indexer.service.ts:17).
DELETE /teachers/:id — deactivate (S8)
- 200
dataabsent (void); soft-deleteisDeleted/deletedAt/deletedBy+version+1(base.repository.ts:68-74). - 404 "Teacher not found." (
teacher.service.ts:95-97). - Side-effects:
TeacherDeleted→audit-writelog-teacher-deleted(event-queue-map.ts:33); search index removal (search-indexer.service.ts:24,52-59).
2. Subject assignments — teaching matrix (S3/S7)
POST /subject-assignments — create (S7)
- Body:
CreateSubjectAssignmentDto(create-subject-assignment.dto.ts:4-19, F3). - 200
data: SubjectAssignment. 400 validation. No 404/409 paths — no existence or duplicate checks (subject-assignment.service.ts:13-17) (OQ-2).
GET /subject-assignments/by-teacher/:teacherId?academicYearId= (S3)
academicYearIdrequired in practice (missing →[]since filter includes it,subject-assignment.service.ts:26-31).- Success:
data: SubjectAssignment[]— non-paginated array, nometa. - Sort: insertion order (no index sort).
GET /subject-assignments/by-class/:classId?academicYearId= (coordinator view)
- Same shape; filter
{classId, academicYearId}(subject-assignment.service.ts:19-24).
DELETE /subject-assignments/:id — remove
- Soft-delete (
subject-assignment.service.ts:33-36); 404 "Assignment not found.". - Caveat:
subject_assignmentshas notimestamps: true(subject-assignment.schema.ts:7) andisDeletedscoping comes fromBaseSchema.
3. Supporting catalogs (read-only for this module)
| Endpoint | Used by | Source |
|---|---|---|
GET /departments (paginated) | F1/F2 department picker, S1 filter | department.controller.ts:29-31 |
GET /designations (paginated) | F1/F2 designation picker | designation.controller.ts |
GET /subjects (paginated) | F1 subjects multi, S7 subject picker | subject.controller.ts:27-29 |
GET /classes (+ by-year/:academicYearId) | F1 classTeacherFor multi, S7 class picker | class.controller.ts:27-33 |
GET /academic-years | S3/S7 year picker (isCurrent flag) | academic-year.controller.ts:27-30 |
GET /timetable?teacherId= | S4 schedule | timetable.controller.ts:21-27 |
GET /dashboard/overview | teachers.total KPI | dashboard.service.ts:32,52 |
GET /users | F5 user picker | Users module |
4. Loading / streaming / realtime
| Screen | Loading | Realtime |
|---|---|---|
| S1 list | skeleton rows | — (teacher.created WS event (planned)) |
| S2/S3/S4 detail | skeleton card / rows | refresh on notification.new for own tenant (planned) |
| S5/S6/S7 forms | submit spinner only | — |
| S8 dialog | button spinner | — |
5. Client error mapping table (module)
| Screen | Code | UI |
|---|---|---|
| S1/S2/S3 | 401 | silent refresh; fail → sessionExpired |
| S1/S2/S3 | 403 | 403 screen (future server enforcement) |
| S2/S3/S8 | 404 | "Teacher not found or removed" → back |
| S3 remove | 404 | treat as removed locally |
| S5 | 409 | inline conflict banner, keep form |
| S5/S6/S7 | 400 | per-field errors |
| all | 429 | "Try again in a moment", no auto-retry |
| all | 5xx | generic + requestId + retry |
6. Pagination summary
GET /teachers: paginated (page/limit/meta) — infinite scroll.GET /subject-assignments/by-*,GET /timetable, catalogs: non-paginated arrays except catalogs which paginate viaPaginationQueryDto— client treats catalogs as page-through (limit 100) or caches 24 h.GET /departments//designationspaginate — client loads withlimit=100and caches.
7. Optimistic / undo
- List refresh + detail refresh: server-first (read-after-write on navigation back).
- Status change (F2) is a
$set— safe; client applies after 200 only (no rollback complexity). - No destructive op is optimistic (00-shared/06 §3.5); UNDO snackbar not offered for deactivate (irreversible by design) — offering undo would be inventing an API surface (no reactivation endpoint).