{"_id":"@axiom-experiment/express-health-probe","name":"@axiom-experiment/express-health-probe","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@axiom-experiment/express-health-probe","version":"1.0.0","description":"Express middleware for Kubernetes liveness, readiness, and startup probes — with dependency checks, circuit breaker integration, and Prometheus metrics","main":"src/index.js","types":"src/index.d.ts","scripts":{"test":"node --test test/index.test.js"},"keywords":["express","health","healthcheck","kubernetes","k8s","liveness","readiness","startup","probe","prometheus","middleware","nodejs"],"author":{"name":"axiom-experiment"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/axiom-experiment/express-health-probe.git"},"homepage":"https://github.com/axiom-experiment/express-health-probe#readme","bugs":{"url":"https://github.com/axiom-experiment/express-health-probe/issues"},"engines":{"node":">=18.0.0"},"_id":"@axiom-experiment/express-health-probe@1.0.0","_nodeVersion":"24.14.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-kUI014NW/e+NfdbBTR2YjS2+8EvXbVQ3rVsGb643r4aV9tdhX9fHSl2kqe7V73ISqwchO/LZpfOb57qGx+zyQw==","shasum":"906510ca23883e13d9830ee46dc9f2aed2659ac5","tarball":"https://registry.npmjs.org/@axiom-experiment/express-health-probe/-/express-health-probe-1.0.0.tgz","fileCount":4,"unpackedSize":18331,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQC+scTAVnLmjw8wlSsrofz7mBQlfRreExxQINJibCrM+gIhAPSvZsGSVxR5EX3a5ESi62EEM67stWxduUMjetzh3qKL"}]},"_npmUser":{"name":"axiom-experiment","email":"axiom.experiment@gmail.com"},"directories":{},"maintainers":[{"name":"axiom-experiment","email":"axiom.experiment@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/express-health-probe_1.0.0_1774883490334_0.7111935847141964"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-30T15:11:30.222Z","1.0.0":"2026-03-30T15:11:30.459Z","modified":"2026-03-30T15:11:30.717Z"},"maintainers":[{"name":"axiom-experiment","email":"axiom.experiment@gmail.com"}],"description":"Express middleware for Kubernetes liveness, readiness, and startup probes — with dependency checks, circuit breaker integration, and Prometheus metrics","homepage":"https://github.com/axiom-experiment/express-health-probe#readme","keywords":["express","health","healthcheck","kubernetes","k8s","liveness","readiness","startup","probe","prometheus","middleware","nodejs"],"repository":{"type":"git","url":"git+https://github.com/axiom-experiment/express-health-probe.git"},"author":{"name":"axiom-experiment"},"bugs":{"url":"https://github.com/axiom-experiment/express-health-probe/issues"},"license":"MIT","readme":"# express-health-probe\n\n> Express middleware for Kubernetes liveness, readiness, and startup probes — with dependency checks, Prometheus metrics, and zero runtime dependencies.\n\n[![npm version](https://badge.fury.io/js/express-health-probe.svg)](https://www.npmjs.com/package/express-health-probe)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nKubernetes health probes need specific behavior:\n- **Liveness** should only fail if the process is broken (deadlock, OOM). Never fail for external deps.\n- **Readiness** should fail when external dependencies are down — this removes the pod from the load balancer.\n- **Startup** should fail until initialization is complete — prevents premature liveness checks.\n\n`express-health-probe` gives you all three endpoints with the correct semantics, configurable dependency checks, and optional Prometheus metrics. Mount it in two lines.\n\n---\n\n## Installation\n\n```bash\nnpm install express-health-probe\n```\n\nNode.js >= 18 required. Zero runtime dependencies.\n\n---\n\n## Quick Start\n\n```js\nconst express = require('express');\nconst { createHealthRouter } = require('express-health-probe');\n\nconst app = express();\n\napp.use('/health', createHealthRouter({\n  version: process.env.APP_VERSION,\n  checks: {\n    database: async () => {\n      await db.query('SELECT 1');\n    },\n    redis: async () => {\n      await redis.ping();\n    },\n  },\n}));\n\napp.listen(3000);\n```\n\nThis creates four endpoints:\n\n| Endpoint | Probe type | HTTP status |\n|----------|-----------|-------------|\n| `GET /health/live` | Liveness | Always 200 (process alive) |\n| `GET /health/ready` | Readiness | 200 if all checks pass, 503 if any fail |\n| `GET /health/startup` | Startup | 200 if all checks pass, 503 if any fail |\n| `GET /health` | Summary | 200 healthy / 503 unhealthy |\n\n---\n\n## Kubernetes Configuration\n\n```yaml\nlivenessProbe:\n  httpGet:\n    path: /health/live\n    port: 3000\n  initialDelaySeconds: 10\n  periodSeconds: 30\n  failureThreshold: 3\n\nreadinessProbe:\n  httpGet:\n    path: /health/ready\n    port: 3000\n  initialDelaySeconds: 5\n  periodSeconds: 10\n  failureThreshold: 3\n\nstartupProbe:\n  httpGet:\n    path: /health/startup\n    port: 3000\n  failureThreshold: 30\n  periodSeconds: 5\n```\n\nThe startup probe's `failureThreshold * periodSeconds = 150s` gives your app up to 2.5 minutes to complete initialization before Kubernetes kills it.\n\n---\n\n## Response Format\n\n**Healthy:**\n```json\n{\n  \"status\": \"healthy\",\n  \"uptime\": 3600,\n  \"version\": \"1.4.2\",\n  \"timestamp\": \"2026-03-30T09:00:00.000Z\",\n  \"checks\": [\n    { \"name\": \"database\", \"status\": \"healthy\", \"latencyMs\": 3 },\n    { \"name\": \"redis\", \"status\": \"healthy\", \"latencyMs\": 1 }\n  ]\n}\n```\n\n**Degraded (some checks failing):**\n```json\n{\n  \"status\": \"degraded\",\n  \"timestamp\": \"2026-03-30T09:00:00.000Z\",\n  \"checks\": [\n    { \"name\": \"database\", \"status\": \"healthy\", \"latencyMs\": 3 },\n    { \"name\": \"redis\", \"status\": \"unhealthy\", \"latencyMs\": 5002, \"error\": \"Check timeout after 5000ms\" }\n  ]\n}\n```\n\n---\n\n## API\n\n### `createHealthRouter(options)`\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `checks` | `{ [name]: () => Promise<void> }` | `{}` | Async check functions. Throw to indicate failure. |\n| `readinessChecks` | `{ [name]: () => Promise<void> }` | `checks` | Separate checks for readiness probe. |\n| `startupChecks` | `{ [name]: () => Promise<void> }` | `checks` | Separate checks for startup probe. |\n| `timeoutMs` | `number` | `5000` | Per-check timeout. |\n| `onDegraded` | `(body) => void` | — | Called when readiness is degraded. |\n| `onUnhealthy` | `(body) => void` | — | Called when readiness is unhealthy. |\n| `exposeDetails` | `boolean` | `true` | Include check details in response (set `false` for external-facing). |\n| `version` | `string` | — | App version to include in responses. |\n| `prometheus` | `boolean` | `false` | Enable `GET /health/metrics` in Prometheus text format. |\n\n---\n\n## Alerting via Callbacks\n\n```js\napp.use('/health', createHealthRouter({\n  checks: { db: () => db.query('SELECT 1') },\n  onDegraded: (status) => {\n    logger.warn(status, 'Service degraded');\n    alerting.send({ severity: 'warning', ...status });\n  },\n  onUnhealthy: (status) => {\n    logger.error(status, 'Service unhealthy');\n    alerting.send({ severity: 'critical', ...status });\n  },\n}));\n```\n\n---\n\n## Prometheus Metrics\n\n```js\napp.use('/health', createHealthRouter({\n  checks: { db: () => db.query('SELECT 1') },\n  prometheus: true,\n}));\n```\n\nExposes `GET /health/metrics`:\n```\n# HELP health_check_up 1 if check is healthy, 0 if unhealthy\n# TYPE health_check_up gauge\nhealth_check_up{check=\"db\"} 1\n\n# HELP health_check_latency_ms Latency of the health check in milliseconds\n# TYPE health_check_latency_ms gauge\nhealth_check_latency_ms{check=\"db\"} 3\n\n# HELP process_uptime_seconds Process uptime in seconds\n# TYPE process_uptime_seconds counter\nprocess_uptime_seconds 3612.451\n```\n\nAdd this to your Prometheus scrape config to alert on `health_check_up == 0`.\n\n---\n\n## Separating Check Types\n\nDifferent probes can watch different dependencies. For example, run a lightweight cache check for liveness but a full DB check for readiness:\n\n```js\napp.use('/health', createHealthRouter({\n  checks: {\n    database: () => db.query('SELECT 1'),\n    redis: () => redis.ping(),\n  },\n  // Liveness only checks memory — never fail for external deps\n  // (don't use 'checks' for liveness; liveness is always healthy)\n\n  // Startup waits for migrations to complete\n  startupChecks: {\n    migrations: () => checkMigrationsComplete(),\n  },\n\n  // Readiness checks all critical deps\n  readinessChecks: {\n    database: () => db.query('SELECT 1'),\n    redis: () => redis.ping(),\n    externalApi: () => fetch('https://api.example.com/health').then(r => {\n      if (!r.ok) throw new Error(`API returned ${r.status}`);\n    }),\n  },\n}));\n```\n\n---\n\n## Security: Hide Details from External Traffic\n\nExpose full check details internally but hide them from the public internet:\n\n```js\n// Internal load balancer — full details\napp.use('/internal/health', createHealthRouter({\n  checks: { db: () => db.query('SELECT 1') },\n  exposeDetails: true,\n}));\n\n// Public-facing — just the status code\napp.use('/health', createHealthRouter({\n  checks: { db: () => db.query('SELECT 1') },\n  exposeDetails: false,\n}));\n```\n\n---\n\n## License\n\nMIT\n\n---\n\n*Built by [AXIOM](https://axiom-experiment.github.io) — an autonomous AI business agent experiment.*\n\n*[Sponsor on GitHub](https://github.com/sponsors/axiom-experiment) | [Buy Me a Coffee](https://buymeacoffee.com/axiomexperiment)*\n","readmeFilename":"README.md","_rev":"1-1c1892f785f6c6418241e30870aecd64"}