12 - API Mapping (Health Module)
- 1. Health check (implemented)
- 2. Success response - 200 (all up)
- 3. Failure response - 503 (any dependency down)
- 4. Partial failure semantics (200 never happens; 503 with healthy siblings)
- 5. Error map
- 6. Planned / not yet in source
Exact wire contracts for the Health module. Base path
/api/v1(URI versioning,main.ts:44-48; prefixapi, version1-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 thepublictier (health.controller.ts:16,rate-limit.constants.ts:4). Envelopes per 00-shared/07 §2-3.
1. Health check (implemented)
| Method | Path | Auth | Rate | Source |
|---|---|---|---|---|
| GET | /api/v1/health | public (@Public()) | 30 req/min (public tier) | health.controller.ts:27-37 |
| GET | /api/v2/health | public | 30 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 key | Mechanism | Detail payload | Source |
|---|---|---|---|
mongodb | Terminus MongooseHealthIndicator.pingCheck('mongodb') | - | health.controller.ts:32 |
redis | RedisHealthIndicator.check() - PING must return PONG | { ping: 'PONG' } when up | redis-health.indicator.ts:12-23 |
storage | StorageHealthIndicator.check() - provider healthCheck() | - | storage-health.indicator.ts:16-26, storage-provider.ts:27 |
bullmq | BullMqHealthIndicator.check() - getJobCounts on emails, in-app, webhook-deliver, dlq | { pendingJobs: [n,n,n,n] } when up | bullmq-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": ""
}
pendingJobsorder = 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).requestIdechoesx-request-idheader 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
| Code | Meaning | Source |
|---|---|---|
| 200 | all indicators up | health.controller.ts:31-36 |
| 503 | ≥ 1 indicator down (Terminus throws) | @HealthCheck() decorator |
| 429 | rate cap - 30 req/min sliding window, Redis-backed | rate-limit.constants.ts:4, IMPLEMENTATION_PLAN.md:48 |
| 400 | malformed request path/version edge | main.ts:50-57 (global pipes) |
6. Planned / not yet in source
| Path / feature | Status | Source |
|---|---|---|
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 |