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

Exact wire contract for every screen → endpoint. Base /api/v1; envelope per 00-shared/07. All endpoints from src/modules/parents/controllers/parent.controller.ts; business rules from parent.service.ts and student-parent-link.service.ts. Global guards: RateLimitGuardJwtAuthGuardRbacGuard (app.module.ts:129-131); no endpoint carries @Permissions metadata — parent endpoints are effectively JWT-only (OQ-8).


0. Module-wide request envelope & client policy

AspectContract
Basehttps://api.<domain>/api/v1
HeadersAuthorization: Bearer <accessToken>; x-request-id client-generated; Content-Type: application/json
success{success:true, message:"OK", data, meta?, timestamp, requestId} (response-envelope.interceptor.ts:47-60)
error{success:false, message, error:{code, details?}, timestamp, requestId} (http-exception.filter.ts:73-81)
Codes400 VALIDATION_ERROR / 401 UNAUTHENTICATED / 403 PERMISSION_DENIED / 404 RESOURCE_NOT_FOUND / 409 DUPLICATE_RESOURCE / 422 BUSINESS_RULE_VIOLATION / 429 RATE_LIMITED / 5xx INTERNAL_SERVER_ERROR (http-exception.filter.ts:27-35)
TenancytenantId from JWT only (jwt-auth.guard.ts:44-55); injected by repository (base.repository.ts:33-35) — never in body
Rate tierapi 100/min (global RateLimitGuard, prod-only)
Cachingnone server-side for parents; client last-good cache (list)
Offlinereads cached; writes blocked
Retrybackoff on 5xx/network; no auto-retry on 429

Screen: Parents list

EndpointGET /parents
Querypage (1-based), limit (1–100, default 20), sort, qonly page/limit honored (parent.service.ts:58-61; pagination-query.dto.ts:5-30)
Success200 data: ParentDocument[], meta:{page,limit,totalItems,totalPages,hasNext,hasPrevious}
Parent doc shape{_id, tenantId, userId, occupation?, company?, annualIncome?, relationshipNotes?, emergencyContactPriority, pickupAuthorization, metadata?, createdAt, updatedAt, createdBy?, updatedBy?, version} (parent.schema.ts:8-32, base.schema.ts:8-35)
Client flowpage 1 → infinite scroll while meta.hasNext; RefreshIndicator reset
Errors400 bad page/limit; 401; 429; 5xx

Screen: Parent detail

EndpointGET /parents/:id → 200 data: ParentDocument; 404 RESOURCE_NOT_FOUND "Parent not found." (parent.service.ts:49-53)
EndpointGET /parents/:id/students → 200 data: StudentParentLinkDocument[] (raw link docs, parent.service.ts:69-72)
Link doc shape{_id, tenantId, studentId, parentId, relationship, isPrimaryGuardian, financialResponsibility, pickupAllowed, emergencyPriority, metadata?, createdAt, updatedAt, version} (student-parent-link.schema.ts:16-41)
Joinclient fetches GET /students/:id per studentId (OQ-10)
Errors404 (parent); 400 invalid id (CastError → "Invalid resource identifier.", http-exception.filter.ts:48,92)

Screen: Create parent

EndpointPOST /parents body CreateParentDto (create-parent.dto.ts)
RequireduserId (@IsMongoId)
Optionaloccupation, company, annualIncome, relationshipNotes, emergencyContactPriority, pickupAuthorization
Success200/201 data: ParentDocument (Nest default 201 for @Post; envelope interceptor doesn't alter status — verify in e2e)
Side effectParentCreated event → in-app queue job parent-created (parent.service.ts:38-45, event-queue-map.ts:37)
Errors400 validation; 409 DUPLICATE_RESOURCE "Parent profile already exists for this user." (parent.service.ts:32-34); 429; 5xx

Screen: Edit parent

EndpointPATCH /parents/:id body UpdateParentDto (update-parent.dto.ts) — partial $set; userId not accepted
Success200 data: ParentDocument (updated; version incremented, base.repository.ts:62-65)
Side effectParentUpdatedaudit-write log-parent-updated (parent.service.ts:78-86, event-queue-map.ts:38)
Errors400; 404 "Parent not found." (parent.service.ts:77); 429; 5xx

Screen: Delete parent (soft)

EndpointDELETE /parents/:id
Success200 data: undefined (void handler, parent.service.ts:89-100)
Side effectParentDeletedaudit-write log-parent-deleted (event-queue-map.ts:39); links are NOT touched (OQ-3)
Errors404 "Parent not found." (parent.service.ts:91)
EndpointPOST /parents/link/:studentId body LinkParentDto (link-parent.dto.ts) — studentId in the URL, not body
RequiredparentId (@IsMongoId), relationship (string — enum not validated in DTO, OQ-6)
OptionalisPrimaryGuardian, financialResponsibility, pickupAllowed (default true), emergencyPriority
Success200/201 data: StudentParentLinkDocument
Validation realitystudentRepo.findById(studentId) result discarded — missing student does not 404 (OQ-3); duplicates allowed (OQ-2)
Errors400 (bad ids/fields); 429; 5xx (invalid relationship → Mongoose ValidationError → 500, OQ-6)

Screen: Guardians of a student (student-detail embed)

EndpointGET /parents/link/student/:studentId → 200 data: StudentParentLinkDocument[] (student-parent-link.service.ts:34-36)
Joinclient fetches GET /parents/:id per parentId
EndpointDELETE /parents/link/:linkId
Success200 data: undefined (void, student-parent-link.service.ts:38-41) — soft delete
Errors404 RESOURCE_NOT_FOUND "Link not found." (student-parent-link.service.ts:40)

Loading / streaming / realtime

ScreenLoadingStreamingRealtime
parents listAppSkeleton(list)
parent detailheader skeleton + children skeletonschildren load after header
create/edit/linkCTA spinner
my children (forward-looking)skeleton(planned) WS parent.linked

Client-side error mapping table (module)

ScreencodeUI
create409banner + "Open existing profile"
any form400per-field details
detail/edit404AppErrorState + back
unlink404treat-as-removed
link500 (invalid relationship)generic + requestId (client prevents via dropdown)
any401silent refresh → session expiry
any429countdown
any5xxgeneric + requestId + retry

Pagination

GET /parents is the only paginated endpoint (meta as above). Link lists (GET /parents/:id/students, GET /parents/link/student/:studentId) are non-paginated full arrays (student-parent-link.repository.ts:20-26) — client renders all; per-student join is bounded by household size.

Optimistic / undo

  • No optimistic writes on any parents mutation — link/unlink are consequential; server-confirm everywhere (00-shared/06 §3.5).
  • Undo: none (no server endpoint reverses an unlink; soft-deleted links are not restorable via API).
  • Read lists: RefreshIndicator always bypasses client cache.