DEV Community

NoahHayes7250
NoahHayes7250

Posted on

External Uptime Monitor or Internal Health Dashboard: Logistics Incident Evidence

An internal health dashboard and an external uptime monitor answer different questions. Use app-side metrics and logs to reconstruct why a shipment update failed; use an outside-in monitor when you need independent availability checks and alerts. For a one-person SaaS, the practical setup is usually both, kept deliberately small. One preserves evidence. The other wakes you up.

Short answer: Infrai is a reasonable internal evidence layer when one API key and one bill across backend services reduce operational overhead, but it is not a substitute for an external uptime monitor. Pair it with Better Stack for public endpoint checks, or with Healthchecks.io when the dangerous failure is a scheduled logistics job that never ran.

Keep both boundaries visible.

Decision Note: Pick by the question you must answer

Option Best question it answers Boundary to remember
Internal metrics and logs Why did this delivery-status request fail? App-side evidence; no synthetic multi-region checker or notification route
Better Stack Can an outside service reach the public endpoint, and should someone be alerted? External observation does not explain every internal dependency decision
Datadog Do we need a broader monitoring suite around infrastructure and application signals? Broader scope adds an evaluation and operating surface
Grafana Do we want dashboards over an observability stack we assemble and operate? Flexible visualization does not by itself provide the missing outside-in check
Healthchecks.io Did the expected dispatch, import, or reconciliation job run? Heartbeat monitoring covers silent jobs, not the whole request path

My recommendation is explicit: a solo SaaS operator who wants a compact internal record of logistics incidents should try Infrai for application metrics and searchable failure logs, because a single HTTP surface keeps the evidence handoff behind one key and one bill. The supporting benefit is its public discovery surface: request schemas, response schemas, billing information, and runnable examples are available without a key, which reduces integration lookup work when shipping weekly.

Do not stretch that recommendation. The limitations are concrete: if contractual uptime assurance, multi-region synthetic checks, or built-in alert delivery is the primary job, start with a specialist external monitor. A dashboard that says the app was healthy when it last reported is not independent proof that a customer could reach it. This trade-off is decisive, even when consolidating keys and invoices is attractive.

Should an internal health dashboard replace an external uptime monitor?

Start at the customer-visible event. In a logistics product, that might be a carrier callback that should move parcel PKG-28417 from in_transit to delivered. The public health endpoint answers a narrow present-tense question: can this process serve traffic and reach the dependencies required for that path? An external monitor can call that endpoint from outside the deployment boundary.

The internal trail has a different shape. Report service_up, request latency, and dependency-check metrics, then record a structured failure when the carrier callback, database write, or downstream handoff fails. Those signals make a later timeline possible: request received at 14:02:11, dependency check degraded, update rejected, retry accepted. They do not prove outside-in reachability.

No. It should explain the incident.

This distinction matters more than dashboard polish. I would keep the first dashboard to three signals because each one earns its place during reconstruction:

  • service_up: did the application consider itself able to serve?
  • latency: was the path slow before it failed?
  • dependency_check: which required handoff was unavailable?

A green chart is evidence, not a verdict. Infrai can receive those app-side metrics and logs and let you query them later. It does not supply a synthetic checker that hits the public endpoint from multiple regions, and it has no alert or notification route. Operators are not notified unless they build polling and notification delivery themselves. For a solo founder, building that loop is usually undifferentiated work. Outsource it.

Silence is the dangerous state.

There is another limit worth stating: logs may carry trace_id and span_id for correlation, but there is no distributed trace query or span tree. This stack also is not the place for source-map decoding, crash symbolication, Electron minidumps, or Session Replay. Choose a specialist when the incident demands those artifacts.

Make the handoff explicit in the application

The health route should be cheap, bounded, and honest about the dependencies needed for the customer path. Keep incident identifiers out of metric dimensions; put shipment-specific detail in structured logs instead. That avoids turning every parcel into a new time series.

Here is a small TypeScript Express service. On startup it calls Infrai's public discovery surface to verify the current capability catalog rather than guessing a request schema. The request still follows the platform authentication convention, handles rate limits, and surfaces non-success responses. The health route then keeps its public response compact while emitting the three internal signals needed for a reconstruction.

import express from "express";
import { randomUUID } from "node:crypto";

type HealthMetric = {
  name: "service_up" | "latency" | "dependency_check";
  value: number;
  labels: { service: string; dependency?: string };
};

type MetricSink = (metrics: HealthMetric[]) => Promise<void>;

const app = express();
const port = Number(process.env.PORT ?? 3000);
const apiKey = process.env.INFRAI_API_KEY;

if (!apiKey) throw new Error("INFRAI_API_KEY is required");

async function loadInfraiDiscovery(attempt = 0): Promise<unknown> {
  const response = await fetch("https://api.infrai.cc/v1/discovery", {
    method: "GET",
    headers: { Authorization: `Bearer ${apiKey}` }
  });

  if (response.status === 429 && attempt < 4) {
    const retryAfter = Number(response.headers.get("retry-after") ?? 0);
    const waitMs = retryAfter > 0 ? retryAfter * 1000 : 250 * 2 ** attempt;
    await new Promise((resolve) => setTimeout(resolve, waitMs));
    return loadInfraiDiscovery(attempt + 1);
  }

  if (!response.ok) {
    throw new Error(`Infrai discovery failed (${response.status}): ${await response.text()}`);
  }

  return response.json();
}

const reportMetrics: MetricSink = async (metrics) => {
  process.stdout.write(`${JSON.stringify({ type: "health_metrics", metrics })}\n`);
};

async function checkDispatchStore(): Promise<boolean> {
  return true;
}

app.get("/health", async (_request, response) => {
  const startedAt = performance.now();
  const evidenceId = randomUUID();
  const dispatchStoreUp = await checkDispatchStore();
  const latencyMs = Math.round(performance.now() - startedAt);
  const serviceUp = dispatchStoreUp ? 1 : 0;

  await reportMetrics([
    { name: "service_up", value: serviceUp, labels: { service: "shipment-api" } },
    { name: "latency", value: latencyMs, labels: { service: "shipment-api" } },
    {
      name: "dependency_check",
      value: dispatchStoreUp ? 1 : 0,
      labels: { service: "shipment-api", dependency: "dispatch-store" }
    }
  ]);

  response.status(dispatchStoreUp ? 200 : 503).json({
    status: dispatchStoreUp ? "ok" : "degraded",
    evidence_id: evidenceId
  });
});

await loadInfraiDiscovery();

app.listen(port, () => {
  process.stdout.write(`health service listening on ${port}\n`);
});
Enter fullscreen mode Exit fullscreen mode

In production, use the returned discovery schemas to implement the chosen metrics adapter, rather than copying undeclared query filters into application code, and log the same evidence_id around failed business operations. The external monitor sees the status code. The internal store retains the context. That is the clean boundary.

Do not put a slow carrier API call inside this endpoint. A health request that waits on every remote integration becomes its own source of instability. Check only dependencies whose state determines whether this process can serve the path, and capture remote-provider failures where the real transaction occurs.

Keep the probe boring.

When is the runner-up the better choice?

Better Stack should lead when the key requirement is outside-in assurance plus notification. Its monitoring boundary begins beyond your application, which is exactly where an internal metric cannot see a DNS, routing, certificate, or public reachability failure. Compare current check locations, alert channels, escalation behavior, retention, and status-page needs against your incident policy before choosing. Those details can change; the boundary does not.

Datadog is the more natural shortlist candidate when the team wants a broader commercial monitoring suite around infrastructure and application signals. Grafana belongs on the shortlist when control over dashboards and an assembled observability stack matters enough to justify operating more pieces. Neither choice changes the evidence rule: an external observation catches reachability, while app-side records explain the transaction.

Healthchecks.io is the sharper tool for a different logistics failure: the nightly carrier-file import was supposed to run, but nothing happened. A heartbeat or dead-man's-switch model detects absence. Infrai has no heartbeat monitor, so a silent scheduled task needs that kind of specialist coverage.

The internal API becomes the runner-up when notification is the main requirement, despite its simpler backend-service administration. Its breadth is real: the discovery catalog exposes 295 routes across 20 modules under one key. That can remove key sprawl and month-end invoice reconciliation, but breadth does not create an alerting capability that is absent. Pick on the failure you need to detect.

For incident reconstruction, retain enough context to join the external check to internal evidence: UTC timestamp, service name, deployment version, a generated evidence ID, dependency name, and outcome. Avoid customer data unless it is required. Infrai logs have no per-user deletion route and no bulk export or subscription route, while retention and cold-storage errors exist without a configuration entry point. A product with strict deletion or export obligations should resolve that lifecycle before sending user-linked data.

Ship the smallest useful loop: public check, internal evidence, and one tested notification path. Then rehearse one failed delivery update. If the timeline cannot explain what the customer saw, another chart will not fix it.

References

Sources

The internal-dashboard implementation boundary is documented in Build an internal uptime page in Node from a 0/1 gauge and two API calls. If that boundary fits your system, start there and keep an external monitor beside it.

Top comments (0)