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 (Exams Module)

Exact wire contract for every screen → endpoint. Base /api/v1 (main.ts:44 global prefix); envelope per 00-shared/07_API_Conventions.md. All endpoints from examination.controller.ts and result.controller.ts; business rules from examination.service.ts and result.service.ts. All endpoints are guarded by JwtAuthGuard only (examination.controller.ts:21-24, result.controller.ts:7-10); no per-endpoint RBAC decorators exist yet (OQ-4).


0. Module-wide request envelope & client policy

AspectContract
Basehttps://api.<domain>/api/v1
HeadersAuthorization: Bearer <accessToken>; x-request-id; Content-Type: application/json; Idempotency-Key (UUID) on mark saves
Success{success, message:'OK', data, meta?, timestamp, requestId} (response-envelope.interceptor.ts:11-18, 47-60)
Error{success, message, error:{code, details?}, timestamp, requestId} (http-exception.filter.ts:16-25, 73-81)
TenancytenantId from JWT claim — never in body (00-shared/07 §6)
Paginationpage (1-based), limit (1–100, default 20), sort/q accepted but ignored by exams list (OQ-7); meta {page,limit,totalItems,totalPages,hasNext,hasPrevious} (pagination-query.dto.ts:5-30, 41-55)
Cachinglists: last-good cache TTL 24 h; marks/coverage: 5 min SWR (00-shared/06 §3.3)
Offlinereads cached; mark writes queued with idempotency keys (13_State_Management.md §4); create/edit/publish online-only
Retrybackoff on 5xx/network; no auto-retry on 429

1. S1 — Exams List

EndpointGET /examinations?page=1&limit=20 (examination.controller.ts:30-32)
RequestPaginationQueryDto {page?, limit?, sort?, q?} (pagination-query.dto.ts:5-30) — sort/q ignored by service (OQ-7)
Success200 → data = ExaminationDocument[], meta = pagination meta (examination.service.ts:67-79; buildPaginationMeta, pagination-query.dto.ts:41-55)
Doc shape`_id, tenantId, academicYearId, name, type('midterm'
Errors400 bad page/limit; 401; 429; 5xx
Sourceexamination.service.ts:67-79
Offlinecached list (24 h)

2. S2 — Exam Detail

EndpointGET /examinations/:id (examination.controller.ts:33-35)
Success200 → data = exam doc (shape above)
Errors404 RESOURCE_NOT_FOUND "Examination not found." (examination.service.ts:61-65); cross-tenant/deleted → same 404
EndpointGET /examinations/:id/subjects (examination.controller.ts:51-53)
Success200 → data = ExaminationSubjectDocument[] (examination.service.ts:133-137; examination-subject.repository.ts:20-24) — unpaginated
Slot shape_id, examinationId, subjectId, classId, date, startTime('HH:mm'), endTime('HH:mm'), maximumMarks, passingMarks (examination-subject.schema.ts:9-31)
Endpoint (coverage)GET /results/exam-subject/:examSubjectId (result.controller.ts:21-25) → data = ExaminationResultDocument[] (marked count per slot)
Result shape_id, studentId, examinationSubjectId, marksObtained?, grade?, remarks?, publishedAt?, version, createdAt, updatedAt (examination-result.schema.ts:9-25)
Errors404s as above; 429
Offlinecached header + slots (slots 5 min)

3. S3 — Marks Entry

EndpointPOST /results/exam-subject/:examSubjectId/marks (result.controller.ts:26-31)
RequestEnterMarksDto {studentId, marksObtained, grade?, remarks?} (examination-subject.dto.ts:47-66)
Success201 (create) or 200 (update) → data = result doc; upsert by (tenantId, studentId, examinationSubjectId) unique index (examination-result.schema.ts:31-33); existing doc updated in place (examination.service.ts:147-175); emits MarksEntered {studentId, examinationSubjectId} (examination.service.ts:163-174, 183-193)
Business rulesmarksObtained > subject.maximumMarks404 NotFoundException('Marks cannot exceed maximum.') (examination.service.ts:145-146) — client pre-validates (see 08 §4); slot missing → 404 "Exam subject not found." (examination.service.ts:144); no check of passingMarks, of student↔class membership, or of publish state (OQ-5)
Errors400 VALIDATION_ERROR (bad ids / negative marks); 404 as above; 429; 5xx
Sourceexamination.service.ts:139-195
Offlinequeued with Idempotency-Key = op id; flush FIFO

4. S4 — Add Subject Slot

EndpointPOST /examinations/:id/subjects (examination.controller.ts:45-50)
RequestCreateExaminationSubjectDto {examinationId, subjectId, classId, date(ISO), startTime, endTime, maximumMarks, passingMarks} (examination-subject.dto.ts:11-45); examinationId injected from route (examination.service.ts:49)
Success201 → data = slot doc; emits ExaminationSubjectAdded (examination.service.ts:119-130)
Business rules@Min(1) on both marks fields only; no server check: date/time overlap (OQ-2), duplicate subject (OQ-3), passingMarks ≤ maximumMarks (OQ-6), window ⊆ exam (OQ-6)
Errors400; 404 exam missing (create fails on repository); 429
Sourceexamination.service.ts:112-131

5. S5 — Publish

EndpointPOST /examinations/:id/publish (examination.controller.ts:54-56)
Success200 → data = void (examination.service.ts:203-220); server stamps publishedAt = now on all results of the exam's slots (examination-result.repository.ts:42-51), sets exam status: 'published' (examination.service.ts:209-211); emits ExamResultsPublished {examinationId, subjectCount} (examination.service.ts:212-219) → queue in-app, job results-published (event-queue-map.ts:27)
Business rulesno guards: publishes whatever marks exist (no completeness check); re-callable; does not lock edits (OQ-5); not a DB transaction (two sequential writes)
Errors404 exam missing (updateById no-op on missing id — client treats as refresh); 429
Sourceexamination.service.ts:203-220

6. S6 — Student Results (mine)

EndpointGET /results/student/:studentId (result.controller.ts:16-20)
Success200 → data = ExaminationResultDocument[]unpaginated (result.service.ts:36-38; examination-result.repository.ts:26-28); no publish filter — unpublished marks are returned
Errors401; 429
Noteclient groups rows by exam via slot ids (subject/exam names resolved client-side, OQ-9)

7. S7 — Report Card

EndpointGET /results/report-card/:studentId/:examId (result.controller.ts:32-37)
Success200 → data = ReportCard {studentId, examinationId, subjects:[{subjectId, subjectName(==raw subjectId, OQ-9), marksObtained, maximumMarks, grade?, remarks?}], totalMarksObtained, totalMaximumMarks, percentage(2dp), overallGrade, generatedAt(ISO)} (result.service.ts:8-26, 46-98)
Business rulesno subjects → 404 "No subjects found for this examination." (result.service.ts:51-53); missing marks count as 0 (result.service.ts:70); overallGrade bands A+(≥90)/A(≥80)/B+(≥70)/B(≥60)/C(≥50)/D(≥40)/F (result.service.ts:130-138)
Errors404 as above; 429

8. S8 / S8a — Create / Edit Exam

EndpointPOST /examinations (examination.controller.ts:27-29)
RequestCreateExaminationDto {academicYearId, name, type, startDate, endDate} (examination.dto.ts:4-26); server forces status:'draft' (examination.service.ts:48); emits ExaminationCreated {examinationId, name} (examination.service.ts:50-57)
Success201 → data = exam doc
EndpointPATCH /examinations/:id (examination.controller.ts:36-41)
RequestUpdateExaminationDto {name?, type?, startDate?, endDate?, status?} (examination.dto.ts:28-53); $set only sent fields, version +1 (base.repository.ts:57-66); emits ExaminationUpdated {examinationId, changes:[keys]} (examination.service.ts:88-96)
Errors400; 404 "Examination not found." (examination.service.ts:85-87); free-string status not enum-enforced on PATCH (OQ-5)
Endpoint (delete)DELETE /examinations/:id (examination.controller.ts:42-44) — soft delete (examination.service.ts:99-110, base.repository.ts:68-74); emits ExaminationDeleted; no cascade to slots/results; 404 if already deleted

9. Events & queues (context, not client endpoints)

ItemSource
ExamResultsPublished → queue in-app, job results-publishedevent-queue-map.ts:27
ExaminationCreated/Updated/Deleted, ExaminationSubjectAdded, MarksEntered — emitted but no BullMQ route (no consumers today)examination.service.ts:50-57, 88-96, 102-110, 119-130, 163-174, 183-193; event-queue-map.ts
ExaminationResultCreated (Results module path)result.service.ts:114-126
Queues available (emails, push, whatsapp, in-app, …)queue.constants.ts:1-17
Planned exams endpoints: /:id/schedule, /hall-tickets, /seating-plan, /marks-import, /re-evaluation(planned) docs/IMPLEMENTATION_PLAN.md:213-220
Mock test / DPP / test series (createMockTest, createDPP, createTestSeries, exam type enum extension)(planned) docs/IMPLEMENTATION_PLAN.md:501-567, 643

10. Client error mapping (module)

CodeExams-specific UX
400 VALIDATION_ERRORinline field errors (08 §5); bulk → pre-validate client-side
401 UNAUTHENTICATEDrefresh once; else session expiry (00-shared/06 §5)
403 PERMISSION_DENIEDhide plan/publish/marks actions; read-only state (RBAC (planned), OQ-4)
404 RESOURCE_NOT_FOUNDtwo special cases: "Marks cannot exceed maximum." → refresh slot max + re-enter (examination.service.ts:146); "Exam subject not found." → pop to detail (examination.service.ts:144); others → purge cache + return to list
409 DUPLICATE_RESOURCEnot expected today (no unique constraints on exams/slots, OQ-3); offline flush collision preview
422 BUSINESS_RULE_Violationreserved for future conflict/immutability checks — not emitted by exams service today (OQ-2/OQ-5)
429 RATE_LIMITEDbackoff, no auto-retry
5xxgeneric + requestId; retry; last-good cache