@sebspark/health-check 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,658 @@
1
+ # `@sebspark/health-check`
2
+
3
+ An easy way to add health check routes to your API.
4
+
5
+ ## Features
6
+
7
+ - Exposes standard health endpoints under `/health`
8
+ - `/health/ping` – always returns `{ status: "ok" }`
9
+ - `/health/live` – liveness probe with system and process info
10
+ - `/health/ready` – readiness probe with dependency checks
11
+ - `/health` – combined view
12
+ - Supports critical and non-critical dependencies
13
+ - Throttled dependency checks to avoid overload
14
+
15
+ ## Usage
16
+
17
+ ```ts
18
+ import express from 'express'
19
+ import { HealthMonitor, DependencyMonitor } from '@sebspark/health-check'
20
+
21
+ const app = express()
22
+ const monitor = new HealthMonitor()
23
+
24
+ // Critical inline dependency: Database
25
+ monitor.addDependency('db', new DependencyMonitor({
26
+ impact: 'critical',
27
+ healthyLimitMs: 50, // ≤ 50ms and 'ok' ⇒ ok
28
+ timeoutLimitMs: 500, // > 500ms ⇒ error (aborted)
29
+ syncCall: async () => {
30
+ try {
31
+ const latencyMs = await db.ping() // throws on failure
32
+ // When syncCall returns 'ok' but took > healthyLimitMs, monitor marks it 'degraded'
33
+ return 'ok'
34
+ } catch (err) {
35
+ return err as Error // treated as 'error'
36
+ }
37
+ },
38
+ }))
39
+
40
+ // Non-critical polled dependency: External API
41
+ monitor.addDependency('externalApi', new DependencyMonitor({
42
+ impact: 'non_critical',
43
+ pollRate: 10_000, // run every 10s and cache the result
44
+ healthyLimitMs: 250, // ≤ 250ms and 'ok' ⇒ ok
45
+ timeoutLimitMs: 1000, // > 1000ms ⇒ error (request times out)
46
+ syncCall: async () => {
47
+ try {
48
+ const res = await fetch('https://status.example.com/health', { method: 'GET' })
49
+ // 'ok' within healthyLimitMs ⇒ ok; 'ok' but slower ⇒ degraded; non-2xx ⇒ degraded
50
+ return res.ok ? 'ok' : 'degraded'
51
+ } catch (err) {
52
+ return err as Error
53
+ }
54
+ },
55
+ }))
56
+
57
+ // Critical async dependency: Pub/Sub round-trip (ping/pong)
58
+ monitor.addDependency('pubsub', new DependencyMonitor({
59
+ impact: 'critical',
60
+ pollRate: 15_000, // start a new async round-trip every 15s
61
+ healthyLimitMs: 100, // pong within 100ms ⇒ ok; slower ⇒ degraded
62
+ timeoutLimitMs: 1000, // no pong by 1s ⇒ error
63
+ asyncCall: async (report) => {
64
+ const topic = pubsub.topic('health-topic')
65
+ const sub = topic.subscription('health-sub')
66
+
67
+ // listen for a single pong
68
+ const onMessage = (msg: any) => {
69
+ try {
70
+ const data = JSON.parse(msg.data.toString())
71
+ if (data.type === 'pong') {
72
+ report('ok') // monitor classifies ok/degraded based on elapsed time
73
+ } else {
74
+ report('degraded')
75
+ }
76
+ } catch (e) {
77
+ report(e as Error)
78
+ } finally {
79
+ sub.removeListener('message', onMessage)
80
+ }
81
+ }
82
+ sub.on('message', onMessage)
83
+
84
+ // publish ping
85
+ await topic.publishMessage({ data: Buffer.from(JSON.stringify({ type: 'ping' })) })
86
+ },
87
+ }))
88
+
89
+ app.use(monitor.router)
90
+
91
+ app.listen(3000, () => {
92
+ console.log('Server running on http://localhost:3000')
93
+ })
94
+ ```
95
+
96
+ ## OpenAPI specification
97
+
98
+ ### JSON
99
+
100
+ ```json
101
+ {
102
+ "tags": [
103
+ { "name": "system", "description": "Operational endpoints." }
104
+ ],
105
+ "paths": {
106
+ "/health/ping": {
107
+ "get": {
108
+ "tags": ["system"],
109
+ "summary": "Health ping",
110
+ "description": "Basic health status.",
111
+ "security": [],
112
+ "responses": {
113
+ "200": {
114
+ "description": "Service responds with basic status.",
115
+ "content": {
116
+ "application/json": {
117
+ "schema": { "$ref": "#/components/schemas/HealthCheck_Status" }
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+ },
124
+ "/health/live": {
125
+ "get": {
126
+ "tags": ["system"],
127
+ "summary": "Liveness",
128
+ "description": "Liveness signal with system and process metrics.",
129
+ "security": [],
130
+ "responses": {
131
+ "200": {
132
+ "description": "Liveness payload.",
133
+ "content": {
134
+ "application/json": {
135
+ "schema": { "$ref": "#/components/schemas/HealthCheck_Liveness" }
136
+ }
137
+ }
138
+ }
139
+ }
140
+ }
141
+ },
142
+ "/health/ready": {
143
+ "get": {
144
+ "tags": ["system"],
145
+ "summary": "Readiness",
146
+ "description": "Readiness including dependency checks and summary.",
147
+ "security": [],
148
+ "responses": {
149
+ "200": {
150
+ "description": "Readiness payload.",
151
+ "content": {
152
+ "application/json": {
153
+ "schema": { "$ref": "#/components/schemas/HealthCheck_ReadinessPayload" }
154
+ }
155
+ }
156
+ }
157
+ }
158
+ }
159
+ },
160
+ "/health": {
161
+ "get": {
162
+ "tags": ["system"],
163
+ "summary": "Comprehensive health summary",
164
+ "description": "Combined view of status, system/process metrics, and readiness summary.",
165
+ "security": [],
166
+ "responses": {
167
+ "200": {
168
+ "description": "Health summary.",
169
+ "content": {
170
+ "application/json": {
171
+ "schema": { "$ref": "#/components/schemas/HealthCheck_HealthSummary" }
172
+ }
173
+ }
174
+ }
175
+ }
176
+ }
177
+ }
178
+ },
179
+ "components": {
180
+ "schemas": {
181
+ "HealthCheck_StatusValue": {
182
+ "type": "string",
183
+ "enum": ["ok", "degraded", "error"],
184
+ "description": "Health status value."
185
+ },
186
+ "HealthCheck_Impact": {
187
+ "type": "string",
188
+ "enum": ["critical", "non_critical"],
189
+ "description": "Operational impact if the dependency fails."
190
+ },
191
+ "HealthCheck_Mode": {
192
+ "type": "string",
193
+ "enum": ["inline", "polled", "async"],
194
+ "description": "How the dependency is checked."
195
+ },
196
+ "HealthCheck_Status": {
197
+ "type": "object",
198
+ "properties": {
199
+ "status": { "$ref": "#/components/schemas/HealthCheck_StatusValue" }
200
+ },
201
+ "required": ["status"]
202
+ },
203
+ "HealthCheck_System": {
204
+ "type": "object",
205
+ "properties": {
206
+ "hostname": { "type": "string" },
207
+ "platform": {
208
+ "type": "string",
209
+ "description": "NodeJS.Platform",
210
+ "enum": ["aix","android","darwin","freebsd","linux","openbsd","sunos","win32"]
211
+ },
212
+ "release": { "type": "string" },
213
+ "arch": { "type": "string", "description": "e.g., x64, arm64" },
214
+ "uptime": { "type": "number", "description": "Seconds." },
215
+ "loadavg": {
216
+ "type": "array",
217
+ "items": { "type": "number" },
218
+ "minItems": 3,
219
+ "maxItems": 3,
220
+ "description": "Load averages for 1, 5, 15 minutes."
221
+ },
222
+ "totalmem": { "type": "number" },
223
+ "freemem": { "type": "number" },
224
+ "memUsedRatio": { "type": "number", "minimum": 0, "maximum": 1 },
225
+ "cpus": {
226
+ "type": "object",
227
+ "properties": {
228
+ "count": { "type": "integer", "minimum": 1 },
229
+ "model": { "type": "string" },
230
+ "speedMHz": { "type": "number" }
231
+ },
232
+ "required": ["count"]
233
+ }
234
+ },
235
+ "required": [
236
+ "hostname","platform","release","arch","uptime","loadavg",
237
+ "totalmem","freemem","memUsedRatio","cpus"
238
+ ]
239
+ },
240
+ "HealthCheck_MemoryUsage": {
241
+ "type": "object",
242
+ "description": "Process memory usage (NodeJS.MemoryUsage).",
243
+ "properties": {
244
+ "rss": { "type": "number" },
245
+ "heapTotal": { "type": "number" },
246
+ "heapUsed": { "type": "number" },
247
+ "external": { "type": "number" },
248
+ "arrayBuffers": { "type": "number" }
249
+ },
250
+ "additionalProperties": { "type": "number" }
251
+ },
252
+ "HealthCheck_Process": {
253
+ "type": "object",
254
+ "properties": {
255
+ "pid": { "type": "integer" },
256
+ "node": { "type": "string", "description": "Node.js version string." },
257
+ "uptime": { "type": "number", "description": "Seconds." },
258
+ "memory": { "$ref": "#/components/schemas/HealthCheck_MemoryUsage" }
259
+ },
260
+ "required": ["pid","node","uptime","memory"]
261
+ },
262
+ "HealthCheck_Liveness": {
263
+ "allOf": [
264
+ { "$ref": "#/components/schemas/HealthCheck_Status" },
265
+ {
266
+ "type": "object",
267
+ "properties": {
268
+ "timestamp": { "type": "string", "format": "date-time" },
269
+ "system": { "$ref": "#/components/schemas/HealthCheck_System" },
270
+ "process": { "$ref": "#/components/schemas/HealthCheck_Process" }
271
+ },
272
+ "required": ["timestamp","system","process"]
273
+ }
274
+ ]
275
+ },
276
+ "HealthCheck_Freshness": {
277
+ "type": "object",
278
+ "properties": {
279
+ "lastChecked": { "type": "string", "format": "date-time" },
280
+ "lastSuccess": { "type": ["string","null"], "format": "date-time" }
281
+ },
282
+ "required": ["lastChecked","lastSuccess"]
283
+ },
284
+ "HealthCheck_Observed": {
285
+ "type": "object",
286
+ "properties": {
287
+ "latencyMs": { "type": ["number","null"] }
288
+ },
289
+ "additionalProperties": true
290
+ },
291
+ "HealthCheck_CheckError": {
292
+ "type": "object",
293
+ "properties": {
294
+ "code": { "type": "string" },
295
+ "message": { "type": "string" }
296
+ },
297
+ "required": ["code","message"]
298
+ },
299
+ "HealthCheck_DependencyCheck": {
300
+ "allOf": [
301
+ { "$ref": "#/components/schemas/HealthCheck_Status" },
302
+ {
303
+ "type": "object",
304
+ "properties": {
305
+ "impact": { "$ref": "#/components/schemas/HealthCheck_Impact" },
306
+ "mode": { "$ref": "#/components/schemas/HealthCheck_Mode" },
307
+ "freshness": { "$ref": "#/components/schemas/HealthCheck_Freshness" },
308
+ "observed": { "$ref": "#/components/schemas/HealthCheck_Observed" },
309
+ "details": { "type": "object", "additionalProperties": true },
310
+ "error": { "oneOf": [ { "$ref": "#/components/schemas/HealthCheck_CheckError" }, { "type": "null" } ] },
311
+ "since": { "type": ["string","null"], "format": "date-time" }
312
+ },
313
+ "required": ["impact","mode","freshness"]
314
+ }
315
+ ]
316
+ },
317
+ "HealthCheck_ReadinessSummary": {
318
+ "type": "object",
319
+ "properties": {
320
+ "critical": {
321
+ "type": "object",
322
+ "properties": {
323
+ "ok": { "type": "integer", "minimum": 0 },
324
+ "failing": { "type": "integer", "minimum": 0 }
325
+ },
326
+ "required": ["ok","failing"]
327
+ },
328
+ "nonCritical": {
329
+ "type": "object",
330
+ "properties": {
331
+ "ok": { "type": "integer", "minimum": 0 },
332
+ "degraded": { "type": "integer", "minimum": 0 },
333
+ "failing": { "type": "integer", "minimum": 0 }
334
+ },
335
+ "required": ["ok","degraded","failing"]
336
+ },
337
+ "degradedReasons": {
338
+ "type": "array",
339
+ "items": { "type": "string" }
340
+ }
341
+ },
342
+ "required": ["critical","nonCritical","degradedReasons"]
343
+ },
344
+ "HealthCheck_ReadinessPayload": {
345
+ "allOf": [
346
+ { "$ref": "#/components/schemas/HealthCheck_Status" },
347
+ {
348
+ "type": "object",
349
+ "properties": {
350
+ "timestamp": { "type": "string", "format": "date-time" },
351
+ "service": {
352
+ "type": "object",
353
+ "properties": {
354
+ "name": { "type": "string" },
355
+ "version": { "type": "string" },
356
+ "instanceId": { "type": "string" }
357
+ },
358
+ "additionalProperties": false
359
+ },
360
+ "summary": { "$ref": "#/components/schemas/HealthCheck_ReadinessSummary" },
361
+ "checks": {
362
+ "type": "object",
363
+ "additionalProperties": { "$ref": "#/components/schemas/HealthCheck_DependencyCheck" },
364
+ "description": "Keyed by dependency name."
365
+ }
366
+ },
367
+ "required": ["timestamp","summary","checks"]
368
+ }
369
+ ]
370
+ },
371
+ "HealthCheck_HealthSummary": {
372
+ "allOf": [
373
+ { "$ref": "#/components/schemas/HealthCheck_Status" },
374
+ {
375
+ "type": "object",
376
+ "properties": {
377
+ "timestamp": { "type": "string", "format": "date-time" },
378
+ "summary": { "$ref": "#/components/schemas/HealthCheck_ReadinessSummary" },
379
+ "checks": {
380
+ "type": "object",
381
+ "additionalProperties": { "$ref": "#/components/schemas/HealthCheck_DependencyCheck" }
382
+ },
383
+ "system": { "$ref": "#/components/schemas/HealthCheck_System" },
384
+ "process": { "$ref": "#/components/schemas/HealthCheck_Process" }
385
+ },
386
+ "required": ["timestamp","summary","checks","system","process"]
387
+ }
388
+ ]
389
+ }
390
+ }
391
+ }
392
+ }
393
+ ```
394
+
395
+ ### YAML
396
+
397
+ ```yaml
398
+ tags:
399
+ - name: system
400
+ description: Operational endpoints.
401
+ paths:
402
+ /health/ping:
403
+ get:
404
+ tags: [system]
405
+ summary: Health ping
406
+ description: Basic health status.
407
+ security: []
408
+ responses:
409
+ "200":
410
+ description: Service responds with basic status.
411
+ content:
412
+ application/json:
413
+ schema:
414
+ $ref: '#/components/schemas/HealthCheck_Status'
415
+ /health/live:
416
+ get:
417
+ tags: [system]
418
+ summary: Liveness
419
+ description: Liveness signal with system and process metrics.
420
+ security: []
421
+ responses:
422
+ "200":
423
+ description: Liveness payload.
424
+ content:
425
+ application/json:
426
+ schema:
427
+ $ref: '#/components/schemas/HealthCheck_Liveness'
428
+ /health/ready:
429
+ get:
430
+ tags: [system]
431
+ summary: Readiness
432
+ description: Readiness including dependency checks and summary.
433
+ security: []
434
+ responses:
435
+ "200":
436
+ description: Readiness payload.
437
+ content:
438
+ application/json:
439
+ schema:
440
+ $ref: '#/components/schemas/HealthCheck_ReadinessPayload'
441
+ /health:
442
+ get:
443
+ tags: [system]
444
+ summary: Comprehensive health summary
445
+ description: Combined view of status, system/process metrics, and readiness summary.
446
+ security: []
447
+ responses:
448
+ "200":
449
+ description: Health summary.
450
+ content:
451
+ application/json:
452
+ schema:
453
+ $ref: '#/components/schemas/HealthCheck_HealthSummary'
454
+
455
+ components:
456
+ schemas:
457
+ HealthCheck_StatusValue:
458
+ type: string
459
+ enum: [ok, degraded, error]
460
+ description: Health status value.
461
+
462
+ HealthCheck_Impact:
463
+ type: string
464
+ enum: [critical, non_critical]
465
+ description: Operational impact if the dependency fails.
466
+
467
+ HealthCheck_Mode:
468
+ type: string
469
+ enum: [inline, polled, async]
470
+ description: How the dependency is checked.
471
+
472
+ HealthCheck_Status:
473
+ type: object
474
+ properties:
475
+ status:
476
+ $ref: '#/components/schemas/HealthCheck_StatusValue'
477
+ required: [status]
478
+
479
+ HealthCheck_System:
480
+ type: object
481
+ properties:
482
+ hostname:
483
+ type: string
484
+ platform:
485
+ type: string
486
+ description: NodeJS.Platform
487
+ enum: [aix, android, darwin, freebsd, linux, openbsd, sunos, win32]
488
+ release:
489
+ type: string
490
+ arch:
491
+ type: string
492
+ description: e.g., x64, arm64
493
+ uptime:
494
+ type: number
495
+ description: Seconds.
496
+ loadavg:
497
+ type: array
498
+ items:
499
+ type: number
500
+ minItems: 3
501
+ maxItems: 3
502
+ description: Load averages for 1, 5, 15 minutes.
503
+ totalmem:
504
+ type: number
505
+ freemem:
506
+ type: number
507
+ memUsedRatio:
508
+ type: number
509
+ minimum: 0
510
+ maximum: 1
511
+ cpus:
512
+ type: object
513
+ properties:
514
+ count:
515
+ type: integer
516
+ minimum: 1
517
+ model:
518
+ type: string
519
+ speedMHz:
520
+ type: number
521
+ required: [count]
522
+ required:
523
+ - hostname
524
+ - platform
525
+ - release
526
+ - arch
527
+ - uptime
528
+ - loadavg
529
+ - totalmem
530
+ - freemem
531
+ - memUsedRatio
532
+ - cpus
533
+
534
+ HealthCheck_MemoryUsage:
535
+ type: object
536
+ description: Process memory usage (NodeJS.MemoryUsage).
537
+ properties:
538
+ rss:
539
+ type: number
540
+ heapTotal:
541
+ type: number
542
+ heapUsed:
543
+ type: number
544
+ external:
545
+ type: number
546
+ arrayBuffers:
547
+ type: number
548
+ additionalProperties:
549
+ type: number
550
+
551
+ HealthCheck_Process:
552
+ type: object
553
+ properties:
554
+ pid:
555
+ type: integer
556
+ node:
557
+ type: string
558
+ description: Node.js version string.
559
+ uptime:
560
+ type: number
561
+ description: Seconds.
562
+ memory:
563
+ $ref: '#/components/schemas/HealthCheck_MemoryUsage'
564
+ required: [pid, node, uptime, memory]
565
+
566
+ HealthCheck_Liveness:
567
+ allOf:
568
+ - $ref: '#/components/schemas/HealthCheck_Status'
569
+ - type: object
570
+ properties:
571
+ timestamp:
572
+ type: string
573
+ format: date-time
574
+ system:
575
+ $ref: '#/components/schemas/HealthCheck_System'
576
+ process:
577
+ $ref: '#/components/schemas/HealthCheck_Process'
578
+ required: [timestamp, system, process]
579
+
580
+ HealthCheck_Freshness:
581
+ type: object
582
+ properties:
583
+ lastChecked:
584
+ type: string
585
+ format: date-time
586
+ lastSuccess:
587
+ type: [string, "null"]
588
+ format: date-time
589
+ required: [lastChecked, lastSuccess]
590
+
591
+ HealthCheck_Observed:
592
+ type: object
593
+ properties:
594
+ latencyMs:
595
+ type: [number, "null"]
596
+ additionalProperties: true
597
+
598
+ HealthCheck_CheckError:
599
+ type: object
600
+ properties:
601
+ code:
602
+ type: string
603
+ message:
604
+ type: string
605
+ required: [code, message]
606
+
607
+ HealthCheck_DependencyCheck:
608
+ allOf:
609
+ - $ref: '#/components/schemas/HealthCheck_Status'
610
+ - type: object
611
+ properties:
612
+ impact:
613
+ $ref: '#/components/schemas/HealthCheck_Impact'
614
+ mode:
615
+ $ref: '#/components/schemas/HealthCheck_Mode'
616
+ freshness:
617
+ $ref: '#/components/schemas/HealthCheck_Freshness'
618
+ observed:
619
+ $ref: '#/components/schemas/HealthCheck_Observed'
620
+ details:
621
+ type: object
622
+ additionalProperties: true
623
+ error:
624
+ oneOf:
625
+ - $ref: '#/components/schemas/HealthCheck_CheckError'
626
+ - type: "null"
627
+ since:
628
+ type: [string, "null"]
629
+ format: date-time
630
+ required: [impact, mode, freshness]
631
+
632
+ HealthCheck_ReadinessSummary:
633
+ type: object
634
+ properties:
635
+ critical:
636
+ type: object
637
+ properties:
638
+ ok:
639
+ type: integer
640
+ minimum: 0
641
+ failing:
642
+ type: integer
643
+ minimum: 0
644
+ required: [ok, failing]
645
+ nonCritical:
646
+ type: object
647
+ properties:
648
+ ok:
649
+ type: integer
650
+ minimum: 0
651
+ degraded:
652
+ type: integer
653
+ minimum: 0
654
+ failing:
655
+ type: integer
656
+ minimum: 0
657
+ required: [ok, degraded, failing]
658
+ ```