@immera/vitals

Framework-agnostic /vitals health endpoint: one core, thin adapters for Node workers, Express, Next.js, Nuxt, plus a build-time vitals.json generator for static SPAs.
287
Install
npm install @immera/vitals
Latest Version:0.1.0
Vue:3
License:MIT
Last Updated:Jul 30, 2026
Links: npm
Maintainer: promatik

@immera/vitals

One /vitals endpoint for every kind of service: a framework-agnostic core that answers "is this service online, and which build is it running?", plus thin adapters for each transport. Works for HTTP-less workers (RabbitMQ consumers), SSR sites (Next / Nuxt), classic Node/Express services and static SPAs — from a single package, with zero runtime dependencies.

{
  "status": "pass",
  "service": "rabbitmq-worker",
  "version": "1.4.2",
  "commit": "9f2a1c7",
  "environment": "production",
  "timestamp": "2026-07-30T12:00:00.000Z",
  "uptimeSeconds": 8123.4,
  "node": "v22.17.0",
  "memory": { "rss": 74448896, "heapTotal": 18219008, "heapUsed": 12030040, "unit": "bytes" },
  "checks": { "rabbitmq": { "status": "pass", "durationMs": 12 } }
}

The schema follows the IETF Health Check Response Format draft (application/health+json, status: pass | warn | fail) with checks simplified to a flat record. HTTP mapping: pass → 200, warn → 200 (degraded-but-serving; configurable), fail → 503, always with Cache-Control: no-store.

Install

npm install @immera/vitals

Requires Node >= 22.11. Framework peers (express, next, vue, react, h3) are optional — you only need the one whose subpath you import. A worker that imports only @immera/vitals/node never pulls in any of them.

Core

import { createVitals } from "@immera/vitals";

const vitals = createVitals({ service: "rabbitmq-worker" });

vitals.registerCheck("rabbitmq", async ({ signal }) => {
  await connection.ping({ signal });
});
  • registerCheck(name, fn, opts?) — opts.critical (default true), opts.timeoutMs (default 5000), opts.kind ("readiness" default, or "liveness"). A check that throws or times out becomes fail — it never rejects the snapshot. Return { status: "warn", output: "..." } for partial degradation.
  • snapshot() — runs checks in parallel, derives the overall status: any critical fail → fail; otherwise any warn (or non-critical fail) → warn; otherwise pass.
  • livenessSnapshot() — synchronous, runs no dependency checks; maps to Kubernetes /livez ("the process is up").

Metadata resolution precedence: explicit config → environment (IMMERA_VITALS_SERVICE/VERSION/COMMIT/ENV, npm_package_*, GIT_COMMIT, VERCEL_GIT_COMMIT_SHA, GITHUB_SHA, NODE_ENV) → nearest package.json → defaults. The core never statically imports Node builtins, so it also runs on Next's edge runtime (runtime fields are simply omitted there).

Workers without HTTP (queue consumers, cron)

import { createVitals } from "@immera/vitals";
import { startVitalsServer } from "@immera/vitals/node";

const vitals = createVitals({ service: "rabbitmq-worker" });
vitals.registerCheck("rabbitmq", async () => { await conn.ping(); });

const { url, close } = await startVitalsServer({
  vitals,
  port: 9100,                // default: IMMERA_VITALS_PORT, else ephemeral
  livenessPath: "/livez",    // optional k8s probes
  readinessPath: "/readyz",
});
console.log(`vitals at ${url}`);

Only node:http — zero dependencies. createVitalsHandler is also exported to mount the same behaviour inside an existing server.

Express

import { vitalsMiddleware } from "@immera/vitals/express";

app.get("/vitals", vitalsMiddleware(vitals));

Next.js

// App Router — app/vitals/route.ts
export const dynamic = "force-dynamic"; // keep the route out of static caching
export const { GET, HEAD } = createVitalsRoute(vitals);

// Pages Router — pages/api/vitals.ts
export default createVitalsApiHandler(vitals);

Nuxt / Nitro

// server/routes/vitals.get.ts
export default createVitalsEventHandler(vitals); // from "@immera/vitals/nuxt"

Static SPAs (Vue / React without a server)

There is no runtime server, so vitals are generated at build time and served as a static file:

npx @immera/vitals generate --out public/vitals.json --service web --env production

The payload has the same schema (status is always pass, timestamp is the build time, no uptime/memory/checks). runGenerate() from @immera/vitals/generate exposes the same thing programmatically.

Dashboard badge

import { VitalsBadge } from "@immera/vitals/react"; // or "@immera/vitals/vue"

<VitalsBadge url="/vitals" pollMs={30000} />       // static SPAs: url="/vitals.json"

Renders a colored status dot + label, polls the endpoint, and accepts a custom render prop (React) / default slot (Vue).

Package layout

Single package, subpath exports, dual ESM + CJS with types per condition:

Subpath Purpose Peer needed
@immera/vitals / ./core createVitals, serializer, types —
./node sidecar HTTP server / handler —
./express middleware (type-only peer) express at runtime by you
./next App + Pages handlers (type-only peer) next at runtime by you
./nuxt h3 event handler h3
./react / ./vue <VitalsBadge /> react / vue
./generate runGenerate() build-time vitals —

Development

npm run build           # tsup: dual ESM/CJS + .d.ts/.d.cts per entry
npm run typecheck
npm test                # vitest: core + every adapter + clients (jsdom)
npm run check:exports   # publint + attw --pack + dist purity gate
npm run check:fixture   # npm pack → real nodenext consumer: tsc + ESM/CJS/CLI smoke

License

MIT © Immera

Related Packages

nuxt-health

Nuxt health check server middleware

2,250 0
@lamparo/lantern

lamparo's lantern for Node: a signed, read-only route that tells lamparo what th...

457 0
nuxt-healthz

Nuxt module - quick add healthcheck path: /healthcheck to Nuxt App project

2,536 1
h3-express

Express polyfill for h3

5,877 7
@errorgap/nuxt

Nuxt module for Errorgap error tracking (client and server).

988 0

Version History

Version Released Vue License
0.1.0 ^3.3.0 MIT