From beedea229e247f52422389ebb1d93ea5d2c7a5b8 Mon Sep 17 00:00:00 2001 From: "Aaron K. Clark" Date: Tue, 19 May 2026 06:39:10 -0500 Subject: [PATCH] =?UTF-8?q?docs(openapi):=20document=20/healthz=20503=20bo?= =?UTF-8?q?dy=20shape=20=E2=80=94=20db=5Ferror=20field=20was=20undeclared?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The healthz controller emits a `db_error` string on the 503 path (when `SELECT 1` throws), but the OpenAPI spec only had: 503: { description: 'Degraded — DB unreachable' } …with no body schema. SDK generators (openapi-typescript et al.) had no way to surface `db_error` on the client side, even though the controller always sends it on degraded responses. Declare the full 503 body shape — status / db / uptime_s / version / elapsed_ms / migration / db_error — mirroring the 200 path plus the extra error string. Pinned in `tests/api/openapi.test.js` so a future change that drops `db_error` from the controller is forced to also update (or remove) the matching spec field. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/config/openapi.js | 23 ++++++++++++++++++++++- tests/api/openapi.test.js | 19 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/app/config/openapi.js b/app/config/openapi.js index 3fcd408..05006d9 100644 --- a/app/config/openapi.js +++ b/app/config/openapi.js @@ -405,7 +405,28 @@ const spec = { }, }, }, - 503: { description: 'Degraded — DB unreachable' }, + 503: { + description: 'Degraded — DB unreachable', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + status: { type: 'string', enum: ['degraded'] }, + db: { type: 'string', enum: ['down'] }, + uptime_s: { type: 'integer' }, + version: { type: 'string' }, + elapsed_ms: { type: 'number' }, + migration: { type: 'string', nullable: true }, + db_error: { + type: 'string', + description: 'The underlying DB connection error message (e.g. "ECONNREFUSED 127.0.0.1:5432"). Useful for operator debugging; never includes credentials. Present only on the 503 path.', + }, + }, + }, + }, + }, + }, }, }, }, diff --git a/tests/api/openapi.test.js b/tests/api/openapi.test.js index d0ccb38..4c97365 100644 --- a/tests/api/openapi.test.js +++ b/tests/api/openapi.test.js @@ -160,6 +160,25 @@ describe('OpenAPI spec', () => { expect(m.get.responses['200']).toBeDefined(); }); + test('/healthz 503 response documents the db_error field', async () => { + // The healthz controller (app/controllers/healthcontroller.js) + // appends a `db_error` string field to the 503 body when the + // SELECT 1 probe throws. Operators rely on this for debugging; + // SDK generators read it from the spec. Pin the schema so a + // future controller refactor that drops the field also fails + // here and the docs stay in sync with reality. + const res = await request(app).get('/openapi.json'); + const h = res.body.paths['/healthz']; + const r503 = h.get.responses['503']; + expect(r503).toBeDefined(); + const schema = r503.content + && r503.content['application/json'] + && r503.content['application/json'].schema; + expect(schema, '/healthz 503 should declare a body schema').toBeDefined(); + expect(schema.properties.db_error, 'db_error should appear in the 503 body').toBeDefined(); + expect(schema.properties.db_error.type).toBe('string'); + }); + test('GET /docs serves Swagger UI HTML', async () => { const res = await request(app).get('/docs/'); // swagger-ui-express serves HTML; we don't pin the exact body