Skip to content
GuidesDocs 1.3.x

Health Check Protocol ​

Connectum implements the gRPC Health Checking Protocol with additional HTTP endpoints for load balancers and Kubernetes integration.

Configuration ​

The Healthcheck() factory function accepts the following options:

typescript
Healthcheck({
  httpEnabled: true,                              // Enable HTTP health endpoints
  httpPaths: ['/healthz', '/health', '/readyz'],  // HTTP endpoint paths
  watchInterval: 500,                             // Watch polling interval (ms)
  manager: customManager,                         // Custom manager instance
})

Use httpEnabled and httpPaths for platform endpoints, watchInterval for the stream polling cadence, and manager when a test or multi-server process must avoid the singleton. See HealthcheckOptions for the exact field types and defaults.

The healthcheckManager Singleton ​

The healthcheckManager is a global singleton you import from anywhere in your application to update service health status:

typescript
import { healthcheckManager, ServingStatus } from '@connectum/healthcheck';

// Update overall health status
healthcheckManager.update(ServingStatus.SERVING);

// Update a specific service's health status
healthcheckManager.update(ServingStatus.SERVING, 'my.service.v1.MyService');

// Mark as unhealthy
healthcheckManager.update(ServingStatus.NOT_SERVING);

ServingStatus Values ​

StatusValueDescription
UNKNOWN0Status is unknown (initial state)
SERVING1Service is healthy and accepting requests
NOT_SERVING2Service is unhealthy or draining connections
SERVICE_UNKNOWN3Requested service is not registered

Manager Methods ​

MethodDescription
update(status, service?)Update status. Without service, updates all registered entries (services and components). Throws on an unknown name.
register(component, initialStatus?)Register an application health component (default UNKNOWN). Re-registering preserves the current status.
set(component, status)Set a component's status (upsert: registers the component if absent).
unregister(component)Remove a registered component.
getStatus(service)Get status of a specific service or component. Returns ServiceStatus | undefined (undefined for an unknown name — does not throw).
getAllStatuses()Get a Map of all service and component statuses
areAllHealthy()Check if all services and components report SERVING
initialize(serviceNames)Initialize the RPC service slice (called by the protocol's setup, before its first register; in-process transports do not re-run it, a retry after a failed route materialization does). Replaces only service entries; never touches components.
clear()Clear all services and components

gRPC Health Check Protocol ​

The package implements three gRPC methods on the grpc.health.v1.Health service:

The grpcurl commands below require Reflection() and a plaintext HTTP/2 listener (allowHTTP1: false). See Server reflection for setup. Use the HTTP examples with the default plaintext HTTP/1.1 listener.

Health.Check ​

Returns the current health status:

bash
# Check overall health
grpcurl -plaintext localhost:5000 grpc.health.v1.Health/Check

# Check a specific service
grpcurl -plaintext \
  -d '{"service": "my.service.v1.MyService"}' \
  localhost:5000 grpc.health.v1.Health/Check

Health.Watch ​

Streams health status changes in real time:

bash
grpcurl -plaintext localhost:5000 grpc.health.v1.Health/Watch

Behavior follows the gRPC specification:

  • Immediately sends the current status
  • Sends updates only when the status changes
  • For unknown services, sends SERVICE_UNKNOWN (does not terminate the call)
  • Terminates when the client disconnects

Health.List ​

List is part of the gRPC health protocol, alongside Check and Watch. It returns a snapshot of the tracked service statuses.

Lists all tracked services with their statuses. Healthcheck tracks the services mounted before it in protocols; a service that is only declared — for example next to a mounted service in the same .proto file — is not tracked, and Check answers NOT_FOUND for it:

bash
grpcurl -plaintext localhost:5000 grpc.health.v1.Health/List

HTTP Health Endpoints ​

When httpEnabled: true, the following HTTP endpoints are available:

Default Endpoints ​

PathDescription
/healthzOverall health status
/healthOverall health status (alias)
/readyzOverall health status (alias; no separate readiness state)

Response Format ​

json
{
  "status": "SERVING",
  "service": "overall",
  "timestamp": "2026-02-12T10:30:00.000Z"
}

HTTP Status Codes ​

ServingStatusHTTP Code
SERVING200 OK
NOT_SERVING503 Service Unavailable
UNKNOWN503 Service Unavailable
SERVICE_UNKNOWN404 Not Found

Check a Specific Service via HTTP ​

bash
curl http://localhost:5000/healthz?service=my.service.v1.MyService

Custom Paths ​

typescript
Healthcheck({
  httpEnabled: true,
  httpPaths: ['/healthz', '/ready', '/live'],
})

Custom Dependency Health Checks ​

Monitor downstream dependencies (databases, external APIs) alongside your service health:

typescript
import { healthcheckManager, ServingStatus } from '@connectum/healthcheck';

// Application components survive the protocol's RPC-service initialization.
// Component names must be non-empty and contain no dots.
healthcheckManager.register('database');
healthcheckManager.register('cache');

// Periodically check database health
setInterval(async () => {
  try {
    await db.ping();
    healthcheckManager.set('database', ServingStatus.SERVING);
  } catch {
    healthcheckManager.set('database', ServingStatus.NOT_SERVING);
  }
}, 10000);

// Check cache health
setInterval(async () => {
  try {
    await redis.ping();
    healthcheckManager.set('cache', ServingStatus.SERVING);
  } catch {
    healthcheckManager.set('cache', ServingStatus.NOT_SERVING);
  }
}, 10000);

Start these checks only when the dependencies are connected, and clear their intervals in a shutdown hook. Mark each RPC service SERVING separately in the ready listener: an unqualified update(SERVING) also marks dependency components healthy, even before their checks succeed.

Worker-only processes ​

A process with no RPC services (services: []) still needs an explicit readiness source, because an empty registry is never healthy. Register a component before start() and flip it with the server lifecycle:

typescript
import { createServer } from '@connectum/core';
import { Healthcheck, healthcheckManager, ServingStatus } from '@connectum/healthcheck';

const server = createServer({
  services: [],
  port: 5000,
  protocols: [Healthcheck({ httpEnabled: true })],
});

healthcheckManager.register('process');
server.on('ready', () => healthcheckManager.set('process', ServingStatus.SERVING));
server.on('stopping', () => healthcheckManager.set('process', ServingStatus.NOT_SERVING));

await server.start();

/healthz then returns 200 once the component is SERVING, and gRPC Check for process reports the same status, which is what a docker compose service_healthy condition or a Kubernetes probe needs.

Use areAllHealthy() for aggregate health status:

bash
curl http://localhost:5000/healthz
# Returns 503 if ANY dependency reports NOT_SERVING

Isolated Manager for Testing ​

Use createHealthcheckManager() to create isolated instances for tests or multi-server setups:

Bun

Import the runner from bun:test instead of node:test and run the file with bun test.

typescript
import { describe, it } from 'node:test';
import assert from 'node:assert';
import {
  Healthcheck,
  createHealthcheckManager,
  ServingStatus,
} from '@connectum/healthcheck';
import { createServer } from '@connectum/core';

describe('health check', () => {
  it('should track service status', () => {
    const manager = createHealthcheckManager();
    manager.initialize(['my.service.v1.MyService']);

    manager.update(ServingStatus.SERVING, 'my.service.v1.MyService');
    assert.ok(manager.areAllHealthy());

    manager.update(ServingStatus.NOT_SERVING, 'my.service.v1.MyService');
    assert.ok(!manager.areAllHealthy());
  });

  it('should work with createServer', async () => {
    const manager = createHealthcheckManager();

    const server = createServer({
      services: [routes],
      protocols: [Healthcheck({ httpEnabled: true, manager })],
    });

    server.on('ready', () => {
      manager.update(ServingStatus.SERVING);
    });

    await server.start();
    // ... test assertions ...
    await server.stop();
  });
});