2.7 KiB
http-check/http-check
(no submodules)
Purpose
createHttpCheck returns a CheckFn that probes an HTTP endpoint and reports healthy when the response status appears in expectedStatusCodes. It is the standard building block for "is upstream X reachable?" probes — used to check downstream APIs, gateways, internal dependency surfaces, and anything else with a /health or /status endpoint. The factory accepts an injectable fetchImpl so tests never touch real network sockets, and an injectable Clock so responseTime is deterministic.
Features
- Method, headers, and expected-status-code overrides via the
HttpCheckConfigargument. - AbortController-driven per-request timeout that maps to
responseTime+ the human-readable"HTTP check timed out after Xms"message. - Network errors are returned as
unhealthywithdetails.errorcontaining the raw message — not thrown. - Zod boundary validation: empty
url, emptymethod, non-positivetimeoutall throwHealthCheckError(INVALID_OPTIONS). fetchImpldefaults toglobalThis.fetchbound toglobalThis; tests pass a stub.
Usage: Basic
import { createHttpCheck } from "@suaveplan/health-check";
const check = createHttpCheck({ url: "https://upstream.example/health" });
const result = await check();
Usage: Advanced
import { createHttpCheck } from "@suaveplan/health-check";
const check = createHttpCheck(
{
url: "https://internal.api/health",
method: "HEAD",
headers: { authorization: `Bearer ${token}` },
timeout: 1_500,
expectedStatusCodes: [200, 204],
fetchImpl: instrumentedFetch,
},
{ clock: () => performance.now() },
);
API Reference
createHttpCheck(config, options?)→CheckFn.config: HttpCheckConfig—urlrequired;method,headers,timeout,expectedStatusCodes,fetchImploptional.options.clock?: Clock— overridesDate.nowfor response-time measurement.
Implementation Notes
The check never re-throws. Network errors, abort errors, and unexpected status codes are all funnelled into CheckResult objects so the HealthChecker can record them uniformly. Abort detection is case-insensitive on "abort" / "timeout" and also consults controller.signal.aborted so adapters that surface only a generic error message still classify correctly. The responseTime is measured between the clock readings around fetchImpl so it includes DNS, TCP, TLS, and the server's processing time — matching how operators read latency dashboards. The factory is pure: it validates options once and returns a closure that captures the normalised config, so the per-call cost is just the fetch + a small amount of bookkeeping.