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

Exact wire contracts for the Health module. Base path /api/v1 (URI versioning, main.ts:44-48; prefix api, version 1 - env.ts:14,16). Endpoint is @Public() (health.controller.ts:15) so the JWT/RBAC global guards (app.module.ts:129-131) are bypassed; rate-limited on the public tier (health.controller.ts:16, rate-limit.constants.ts:4). Envelopes per 00-shared/07 §2-3.


1. Health check (implemented)

MethodPathAuthRateSource
GET/api/v1/healthpublic (@Public())30 req/min (public tier)health.controller.ts:27-37
GET/api/v2/healthpublic30 req/min@Version(['1','2']), health.controller.ts:28

No params, no body, no query. Four indicators registered in one check (health.controller.ts:31-36):

Indicator keyMechanismDetail payloadSource
mongodbTerminus MongooseHealthIndicator.pingCheck('mongodb')-health.controller.ts:32
redisRedisHealthIndicator.check() - PING must return PONG{ ping: 'PONG' } when upredis-health.indicator.ts:12-23
storageStorageHealthIndicator.check() - provider healthCheck()-storage-health.indicator.ts:16-26, storage-provider.ts:27
bullmqBullMqHealthIndicator.check() - getJobCounts on emails, in-app, webhook-deliver, dlq{ pendingJobs: [n,n,n,n] } when upbullmq-health.indicator.ts:19-37, queue.constants.ts:2,5,14, dlq.constants.ts:1

2. Success response - 200 (all up)

@HealthCheck() + response envelope (response-envelope.interceptor.ts:47-60); shape per HealthCheckResult (health.controller.spec.ts:21-36):

{
  "success": true,
  "message": "OK",
  "data": {
    "status": "ok",
    "info": {
      "mongodb": { "status": "up" },
      "redis":  { "status": "up", "ping": "PONG" },
      "storage":{ "status": "up" },
      "bullmq": { "status": "up", "pendingJobs": [3, 0, 8, 1] }
    },
    "error": {},
    "details": {
      "mongodb": { "status": "up" },
      "redis":  { "status": "up", "ping": "PONG" },
      "storage":{ "status": "up" },
      "bullmq": { "status": "up", "pendingJobs": [3, 0, 8, 1] }
    }
  },
  "timestamp": "2026-08-03T12:04:31.000Z",
  "requestId": ""
}
  • pendingJobs order = injection order: [emails, in-app, webhook-deliver, dlq] (bullmq-health.indicator.ts:21-25); values are per-queue totals (Object.values(r).reduce, :27-29).
  • requestId echoes x-request-id header or is "" (response-envelope.interceptor.ts:44).

3. Failure response - 503 (any dependency down)

@HealthCheck throws ServiceUnavailableException; the global filter renders the error envelope (http-exception.filter.ts:73-81). 503 is not in STATUS_CODE_MAP (:27-35), so code falls to INTERNAL_SERVER_ERROR (:56-58):

{
  "success": false,
  "message": "Redis health check failed",
  "error": {
    "code": "INTERNAL_SERVER_ERROR",
    "details": [ { "message": "Redis health check failed" } ]
  },
  "timestamp": "2026-08-03T12:05:02.000Z",
  "requestId": ""
}

Contract note: the down dependency's key is NOT in this envelope - the HealthCheckResult is only returned on 200. Clients get the culprit from the last successful poll, or from the 503 message string; a (proposed) future change should include the failing key in the error body (14 QA-3).

4. Partial failure semantics (200 never happens; 503 with healthy siblings)

The Terminus result that WOULD be returned (per health.controller.spec.ts:29-35) keeps healthy deps up in info and lists failed keys in error:

{ "status": "error",
  "info":  { "mongodb": { "status": "up" }, "storage": { "status": "up" } },
  "error": { "redis": { "status": "down" }, "bullmq": { "status": "down" } },
  "details": { "mongodb": { "status": "up" }, "redis": { "status": "down" }, … } }

5. Error map

CodeMeaningSource
200all indicators uphealth.controller.ts:31-36
503≥ 1 indicator down (Terminus throws)@HealthCheck() decorator
429rate cap - 30 req/min sliding window, Redis-backedrate-limit.constants.ts:4, IMPLEMENTATION_PLAN.md:48
400malformed request path/version edgemain.ts:50-57 (global pipes)

6. Planned / not yet in source

Path / featureStatusSource
GET /api/v1/health/ready (readiness)(planned)docs/user-flows/END_TO_END_USER_FLOWS.md:739 - no route in health.controller.ts
GET /api/v1/health/live (liveness)(planned)docs/user-flows/END_TO_END_USER_FLOWS.md:740 - no route in health.controller.ts
Disk + memory indicators(planned)Terminus supports (DiskHealthIndicator/MemoryHealthIndicator); not wired - health.controller.ts:31-36
Uptime / version / build hash in payload(planned)HealthCheckResult returned verbatim - health.controller.ts:30-37
Auth'd admin health variant + health.* permission(proposed)no health.* in permissions.constants.ts:1-97 (verified)
Failing-key in 503 error body(proposed)http-exception.filter.ts:56-58, 73-81