Implementing a new probe strategy
All probe strategies live in apps/backend/src/status/application/services/probes/.
Interface
export interface ProbeStrategy {
readonly probeType: string; // must match status_probe_type enum value
run(config: Record<string, unknown>): Promise<ProbeResult>;
}
export interface ProbeResult {
status: StatusLevel;
latencyMs: number;
error?: string;
}
Steps
- Create
my-probe.probe.tsinprobes/implementingProbeStrategy. - Decorate with
@Injectable(). - Enforce a 5s timeout and capture
latencyMs. - Return
{ status, latencyMs, error? }— never throw (catch internally). - Add the class to
StatusModuleproviders underprobeStrategies. - Call
probeRunner.register([..., myProbe])inStatusModule.onModuleInit(). - Add the
probe_typestring value to thestatus_probe_typeDB enum via a migration. - Re-run
pnpm run generate:types.
Example (minimal http probe)
import type { ProbeResult, ProbeStrategy } from "./probe-strategy.interface";
import { Injectable } from "@nestjs/common";
const TIMEOUT_MS = 5_000;
@Injectable()
export class MyProbe implements ProbeStrategy {
readonly probeType = "my_probe";
async run(config: Record<string, unknown>): Promise<ProbeResult> {
const start = Date.now();
try {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
const res = await fetch(config.url as string, { signal: controller.signal })
.finally(() => clearTimeout(timer));
return { status: res.ok ? "operational" : "degraded_performance", latencyMs: Date.now() - start };
} catch (err) {
return { status: "major_outage", latencyMs: Date.now() - start, error: String(err) };
}
}
}
Config schema
Document the expected config fields in a JSDoc on run(). Example:
/**
* @param config.url - Full URL to fetch
* @param config.expectedStatus - Expected HTTP status (default 200)
*/
Timeout enforcement
Every probe must enforce a timeout. Use Promise.race with a reject timer or AbortController. A slow external service must never block the probe cycle — health-probe.processor.ts runs probes concurrently but a stuck promise would leak memory.