Skip to content

Health

Health provides first-party liveness, readiness, startup, and detailed health checks for the bootstrap app host. Endpoint shapes are available in the public alpha and can still change before a stable release.

ts
import { Health, Sloppy } from "sloppy";

const app = Sloppy.create();

app.health()
    .check("self", Health.self(), { tags: ["live", "ready", "startup"] })
    .check("db", Health.data(db), { tags: ["ready"], timeoutMs: 1000, critical: true })
    .check("disk", Health.disk({ path: "./data", minFreeBytes: 500_000_000 }), {
        tags: ["ready", "health"],
        critical: false,
        cacheMs: 5000,
    })
    .expose({
        live: "/live",
        ready: "/ready",
        startup: "/startup",
        health: "/health",
    });

Result Model

Every check returns one of:

  • healthy
  • degraded
  • unhealthy

Top-level aggregation is:

  • any critical unhealthy check makes the response unhealthy;
  • any non-critical unhealthy check makes the response degraded;
  • any critical degraded check with degradedIsUnhealthy: true makes the response unhealthy;
  • any other degraded check makes the response degraded;
  • otherwise the response is healthy.

The response contains deterministic JSON:

json
{
  "status": "degraded",
  "durationMs": 13,
  "checkedAtUtc": "2026-05-12T00:00:00.000Z",
  "checks": {
    "db": {
      "name": "db",
      "status": "healthy",
      "durationMs": 4,
      "checkedAtUtc": "2026-05-12T00:00:00.000Z",
      "tags": ["ready"],
      "critical": true,
      "cached": false,
      "timeoutMs": 1000
    }
  },
  "summary": {
    "healthy": 1,
    "degraded": 0,
    "unhealthy": 0
  }
}

Health output redacts secret-looking keys such as password, secret, token, cookie, authorization, apiKey, and connectionString.

Check Options

app.health().check(name, check, options) accepts:

  • tags: mode selectors such as live, ready, startup, and health;
  • critical: false converts that check's unhealthy result into aggregate degraded;
  • timeoutMs: app-host timeout for one check;
  • cacheMs: app-host cache duration for one check result;
  • degradedIsUnhealthy: for critical checks, treats degraded as aggregate unhealthy.

Built-In Checks

Health.self() reports that the process is alive.

Health.runtime() reports the app-host runtime state and fails when the request context says startup is incomplete or shutdown has started.

Health.config(requiredKeys) checks that required config keys are present. It reports key names only, not values.

Health.data(provider) calls ProviderHealth.check(provider), which runs a bounded provider select 1 probe through the existing data provider facade.

Health.jobs(resource) reports queue or scheduler state from a resource with a state snapshot. Missing scheduler resources produce a degraded result.

Health.disk({ path, minFreeBytes }) checks path accessibility and free bytes when the platform supports statfs.

Health.memory({ degradedRssBytes, unhealthyRssBytes }) checks process memory when the host exposes process.memoryUsage().

Health.rateLimit(store) reports a rate-limit store as healthy, degraded, or unhealthy. Memory stores become unhealthy after disposal. The Redis adapter reports degraded in this build because no first-party Redis provider is present on main; a successful ping() on a supplied Redis-shaped object does not make rate-limit enforcement healthy while check() still fails closed.

Health.http(url, options) and Health.tcp(host, port, options) provide bounded HTTP and TCP dependency probes.

Health.cache(cache) reports safe cache stats and becomes unhealthy after the cache is disposed. Memory caches do not probe another process. Distributed and hybrid caches report the registered cache state through the cache API rather than exposing provider secrets.

Health.redis(redis) calls a Redis client's health() method and reports healthy only when the client can complete its bounded Redis probe. Health.redis(undefined) reports degraded with data.configured: false instead of throwing, which lets optional Redis dependencies appear in readiness output before they are configured.

Compiler Visibility

The bootstrap app host supports every built-in listed above.

Compiler-generated health routes intentionally expose a smaller static surface: inline check functions plus zero-argument Health.self(), Health.runtime(), Health.memory(), and Health.openApi(). Compiler-visible check options are tags, critical, and degradedIsUnhealthy.

The compiler rejects app-host-only built-ins such as Health.config(...), Health.data(...), Health.disk(...), Health.http(...), Health.tcp(...), Health.cache(...), and Health.storage(...) with SLOPPYC_E_UNSUPPORTED_HEALTH_CHECKS. It also rejects compiler-visible timeoutMs and cacheMs instead of silently dropping them.

Endpoint Semantics

app.health().expose() registers four GET endpoints:

EndpointModeDefault checks
/livelivenesschecks tagged live
/readyreadinesschecks tagged ready
/startupstartupchecks tagged startup
/healthdetailedall checks

healthy returns HTTP 200. degraded returns HTTP 200 by default. unhealthy returns HTTP 503.

app.mapHealthChecks() is still supported for the earlier lightweight /health, /health/live, and /health/ready compatibility API. New code should use app.health().

HTTP client checks

Named HTTP clients expose a lightweight health() snapshot. Webhook services also expose health() for storage initialization checks. Backlog and dead-letter thresholds should be modeled as application-owned health checks because acceptable limits are product-specific:

ts
import { Http } from "sloppy/http";

const billing = Http.client("billing", {
    baseUrl: "https://billing.internal",
    circuitBreaker: Http.circuitBreaker({
        failureRatio: 0.5,
        minimumThroughput: 10,
        breakDurationMs: 30000,
    }),
});

app.services.addHttpClient(billing);

app.mapHealthChecks({
    checks: [
        {
            name: "billing-http",
            check: (ctx) => ctx.services.get("http.billing").health().status === "healthy",
            readiness: true,
        },
    ],
});

The built-in snapshot reports unhealthy when the client circuit is open. Probe requests remain application-owned so health checks do not create hidden outbound traffic.

Public alpha. APIs and artifact formats may still change between alpha revisions.