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

Exact wire contract for every screen → endpoint. Base /api/v1; envelope per 00-shared/07 and response-envelope.interceptor.ts / http-exception.filter.ts. All endpoints from src/modules/settings/settings.controller.ts; business rules from settings.service.ts + setting.repository.ts. Global guard chain: RateLimitGuardJwtAuthGuardRbacGuard (app.module.ts:129-131); the controller additionally declares @UseGuards(JwtAuthGuard) (settings.controller.ts:19) and carries no @Permissions metadata (OQ-2 — the global RbacGuard passes because no permission is required, rbac.guard.ts:29).


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}message is always "OK" (response-envelope.interceptor.ts:48-53)
Error{success:false, message, error:{code, details?}, timestamp, requestId} — codes per http-exception.filter.ts:27-35
PaginationNOT paginated. findAll returns a bare array (settings.service.ts:10-12); the interceptor only emits meta when the payload is {data, meta} (response-envelope.interceptor.ts:25-32) → meta is omitted for every settings endpoint. Client renders the full array (no infinite scroll).
TenancytenantId from JWT claim via TenantContextService (setting.repository.ts:16,34); never in body. Platform admin bypasses the tenant scope (base.repository.ts:21-23).
Cachingnone server-side in the settings path; client stale-while-revalidate
Offlinereads from last-good cache + banner; writes blocked (no offline queue)
Retrybackoff on 5xx/network; no auto-retry on 429

E1 — List settings

EndpointGET /settings (settings.controller.ts:24-29)
Query?group= optional, ∈ SettingGroup enum (settings.controller.ts:26; setting.schema.ts:7-14)
Success200 data: Setting[] — sorted group:1, key:1 (settings.service.ts:11); array, no meta
AuthJWT (all roles — permission not enforced, OQ-2)
Errors400 if group not in enum (VALIDATION_ERROR); 401; 429; 5xx
ScreenS1 list / group chips (05 §1, 06 §S1)

Setting doc shape (setting.schema.ts:17-35 + base.schema.ts:10-34):

{
  "_id": "...", "tenantId": "...",
  "key": "attendance.gracePeriod", "value": 10,
  "group": "attendance",
  "label": null, "description": null, "isEncrypted": false,
  "createdBy": null, "updatedBy": null,
  "isDeleted": false, "deletedAt": null, "deletedBy": null,
  "version": 0, "createdAt": "...", "updatedAt": "..."
}

(label/description are schema fields but never written by the service — settings.service.ts:25; isEncrypted is never set — OQ-6.)

E2 — Get setting by key

EndpointGET /settings/:key (settings.controller.ts:31-35)
Success200 data: Setting
404RESOURCE_NOT_FOUND, message Setting "«key»" not found. (settings.service.ts:20)
ScreenS2 detail (06 §S2) — client treats 404 as "not found" empty state

E3 — Upsert setting

EndpointPUT /settings (settings.controller.ts:37-41)
RequestUpdateSettingDto (update-setting.dto.ts:5-26): key (req, unvalidated), value (req, unvalidated — any JSON), group? (@IsEnum), label?/description? (@IsStringdropped by service)
Success200 data: Setting (the saved doc, new: truesetting.repository.ts:39)
SemanticsIdempotent upsert: findOneAndUpdate({tenantId,key,isDeleted:false}, {$set:{value, ...(group?{group}:{})}}, {upsert:true, new:true}) (setting.repository.ts:35-39) — value replaced wholesale, never merged
Errors400 invalid group (details per field); 500 for missing key/value (no DTO decorators → Mongoose required error, OQ-3); 500 E11000 on recreate-after-delete (OQ-5); 401; 429
ScreenS2 save / S3 create (06 §S2-S3)

E4 — Bulk update

EndpointPUT /settings/bulk (settings.controller.ts:43-47)
RequestUpdateSettingDto[] (JSON array)
Success200 data: Setting[] — one doc per input, in input order (settings.service.ts:28-34)
SemanticsSequential per-key upserts — no transaction; a mid-batch failure returns 5xx with earlier keys already persisted (OQ-4)
Clientsend only dirty keys; per-item retry re-sends full dirty set (idempotent)
Screenbatch save (06 §S1 batch mode)

E5 — Delete setting

EndpointDELETE /settings/:key (settings.controller.ts:49-53)
Success200 data: null (void; envelope data = undefined → serialized as omitted/null)
SemanticsSoft delete via BaseRepository.softDeleteisDeleted:true, deletedAt, $inc version (base.repository.ts:68-74); doc remains; unique index still holds the key (setting.schema.ts:38) → recreate fails E11000 → 500 (OQ-5)
Errors404 RESOURCE_NOT_FOUND Setting "«key»" not found. (settings.service.ts:40); 401
ScreenS4 delete confirm (06 §S4) — 404 treated as already removed

E6 — Org embedded settings (design-docs/organizations/12)

  • GET /organizations/:id/settingsdata = org.settings ?? {} (organizations.controller.ts:56-62)
  • PATCH /organizations/:id/settingsfull-replace $set {settings: dto} (organizations.service.ts:129-137) — client must always submit the complete object.

E7 — Feature flags

  • GET /feature-flags[?module=], GET /feature-flags/enabled, GET /feature-flags/:key, PUT /feature-flags, PUT /feature-flags/bulk, DELETE /feature-flags/:key (feature-flags.controller.ts:23-58); flag doc {key, enabled, label?, description?, module?} (feature-flag.schema.ts:9-23).

Loading / streaming / realtime

ScreenLoadingStreamingRealtime
listAppSkeleton(list)— (no WS topic for settings; 00-shared/07 §8 has none)
detailrow/panel skeleton
savebutton/row spinner
batchper-row status

Client-side error mapping table (module)

ScreencodeUI
list/detail401silent refresh → session expiry
list5xxAppErrorState + retry
detail404AppSettingNotFound empty state
save400inline field error (group enum details)
save500generic + requestId, form kept (retry safe — idempotent upsert)
save (E11000 recreate)500banner: key is soft-deleted, cannot recreate (OQ-5)
delete404treat as removed
any429countdown, no auto-retry
any (future)403shared 403 screen — server does not emit today (OQ-2)

Optimistic / undo

  • Explicit-save editors: server-confirm (config values; rollback never needed).
  • Boolean toggle: optimistic with rollback (00-shared/06 §3.5).
  • Delete: no undo (soft-delete not reversible via API).
  • Upsert idempotency makes retries safe everywhere (setting.repository.ts:35-39).