12 — API Mapping (Notifications Module)
- 1. Implemented Endpoints
- 2. Permissions
- 3. Planned / Forward-Looking Endpoints
- 4. Event → Notification Creation Contract (no HTTP)
- 5. Queue-Event Routing (infrastructure)
Exact contract for every endpoint. Base path
/api/v1(URI versioning,src/main.ts). All routes require a JWT (@UseGuards(JwtAuthGuard),notifications.controller.ts:16); success envelope via shared interceptor (common/interceptors/response-envelope.interceptor.ts), errors viacommon/filters/http-exception.filter.ts(see 00-shared/07).
1. Implemented Endpoints
1.1 GET /api/v1/notifications — List my notifications
- Source:
notifications.controller.ts:21-29→service.findMyNotifications(notifications.service.ts:39-52) - Query:
page(int ≥1, default 1),limit(int 1..100, default 20),unreadOnly(bool) —dto/list-notifications.dto.ts:6-25 - Response 200:
{ "data": [ { "_id", "recipientId", "type", "title", "body", "data": {}, "readAt": null, "createdAt", "updatedAt", "version" } ], "meta": { "page", "limit", "totalItems", "totalPages", "hasNext", "hasPrevious" } }metashape frombuildPaginationMeta(common/dto/pagination-query.dto.ts:41-55). - Ordering:
createdAtdesc (repository.ts:66). Filter: tenant + recipient + optionalreadAt: null(repository.ts:52-73). - Errors: 400 (bad query), 401 (no token).
1.2 GET /api/v1/notifications/unread-count — Unread count
- Source:
notifications.controller.ts:31-35→service.getUnreadCount(service.ts:66-70) →repo.countUnreadByRecipient(repository.ts:31-35) - Response 200:
{ "count": 3 }
1.3 PATCH /api/v1/notifications/:id/read — Mark one read
- Source:
notifications.controller.ts:37-41→service.markAsRead(service.ts:54-59) - Ownership:
recipientId !== userId→200 null(no-op, not 404). - Response 200: updated document (with
readAt,version+1—repository.ts:37-43);nullfor foreign/missing id. - Route note: two-segment
:id/readcannot collide withread-all(single segment) — keep this ordering on any future param route.
1.4 PATCH /api/v1/notifications/read-all — Mark all read
- Source:
notifications.controller.ts:43-47→service.markAllAsRead(service.ts:61-64) → bulkupdateManywherereadAt: null(repository.ts:45-50) - Response 200:
{}(envelope with no payload).
2. Permissions
| Permission | Constants | Used by |
|---|---|---|
notification.read | permissions.constants.ts:32 | list / unread-count (planned RBAC binding) |
notification.update | permissions.constants.ts:33 | mark-read endpoints (planned RBAC binding) |
notification.send (internal) | not in ALL_PERMISSIONS | POST /notifications/send — (planned), blueprint Notifications.md:24,68-70 |
notification.preference.manage | not in ALL_PERMISSIONS | preferences — (planned), blueprint Notifications.md:70 |
Note: no RBAC guard is attached today — only
JwtAuthGuard; perms are declared-but-unenforced (gap, see14 §D).
3. Planned / Forward-Looking Endpoints
| Endpoint | Status | Source |
|---|---|---|
POST /api/v1/notifications/send (internal) | (planned) | Notifications.md:24 |
GET /api/v1/notifications/preferences | (planned) | Notifications.md:27 |
PATCH /api/v1/notifications/preferences | (planned) | Notifications.md:28 |
GET /api/v1/notification-templates | (planned) | Notifications.md:29 |
POST /api/v1/notifications/device-token | (future) — (forward-looking) | END_TO_END_USER_FLOWS.md:788 |
4. Event → Notification Creation Contract (no HTTP)
| Domain Event | NotificationType | Title | Source |
|---|---|---|---|
EmailVerified | email_verified | Email Verified | handler.ts:16-20 |
EmailVerificationResent | verification_resent | Verification Email Sent | handler.ts:21-25 |
PasswordResetCompleted | password_reset | Password Reset | handler.ts:26-30 |
UserRegistered | welcome | Welcome to StudyLyon (interpolates firstName, handler.ts:44-47) | handler.ts:41-49 |
Recipient resolution: payload.userId ?? payload.recipientId ?? actorId (handler.ts:76-79).
data = full event payload (handler.ts:96). NotificationCreatedEvent interface
(events/notification-events.ts:1-10) is declared but never emitted (gap 14 §F).
5. Queue-Event Routing (infrastructure)
QUEUE.EMAILS/PUSH/WHATSAPP/IN_APPconstants exist (queue.constants.ts:1-17).event-queue-map.ts:6-43routes events:emails(welcome/reset/invoice/receipt),in-app(UserCreated, OrganizationCreated, Homework*, ExamResultsPublished, Student/Teacher/Staff/ParentCreated, FeeStructureCreated),audit-write, etc.- Gap: no BullMQ worker consumes the
in-appqueue for notifications;NotificationsHandlerlistens on the in-processEventBusonly (handler.ts:61). The map is the (planned) fan-out contract — see14 §G.