A hosted metrics API is a sound choice for a simple Node.js SaaS dashboard when the requirement is to chart custom notification-delivery counters, gauges, and aggregates without operating Prometheus and Grafana. The decisive trade-off is rollback safety: emit immutable delivery facts, deduplicate them before aggregation, and keep the notification provider's operational state separate from the dashboard. A dashboard must never become the authority that decides whether a customer notification was delivered.
Short answer: choose the smallest hosted system that preserves an auditable event identity, supports single and batch reporting, and lets the application query the resulting series. Add a separate heartbeat monitor for jobs that fail silently and a separate alerting path when thresholds must page someone. This boundary is less ambitious than a full observability stack, but it is much easier to reverse.
What must remain true during a rollback?
Suppose release 2026.09.23-3 changes retry classification for a B2B SaaS notification worker. Five minutes later, the failure ratio rises and the release is rolled back. The old and new workers may overlap, the queue may redeliver work, and a batch reporter may retry after losing its response. Counting every attempt as a new fact would make the dashboard look authoritative while quietly double-counting the same delivery.
That is unacceptable for reconciliation.
Give each delivery attempt a stable identifier such as tenant_id + notification_id + attempt_no, and store an append-only audit record before updating a derived metric. A retry with the same identity is a replay, not another failure. The metric dimensions should remain deliberately small: environment, channel, outcome, and release are usually defensible; recipient addresses, free-form error text, and customer IDs create cardinality and compliance problems. In regulated systems, observability data still has retention, access-control, and erasure implications, so the dashboard should receive the minimum data needed to answer an operational question.
The rollback rule is concrete: old and new code may publish the same fact, but they must produce the same idempotency identity and compatible dimensions. If a release changes metric meaning, publish a new metric version rather than rewriting history. A short dual-write window can then compare delivery_failure_ratio_v1 and delivery_failure_ratio_v2, after which the new writer is enabled and the old one is retired.
Should a Node.js SaaS app use a hosted metrics dashboard API?
For each reporting interval, retain four values: attempted deliveries, terminal successes, terminal failures, and still-pending attempts. The invariant is attempted = succeeded + failed + pending. A ratio computed without the pending population can jump merely because a provider is slow, while a ratio computed from mutable status rows can change retrospectively and defeat an audit.
Yes, provided the dashboard is a projection rather than a system of record. Before writing a publisher, inspect the live contract instead of copying an example whose fields may have changed. The following runnable Go program retrieves the public discovery document for metrics.report, uses an explicit method, reads the API key from the environment, checks every response, and backs off on HTTP 429 while honoring Retry-After. The response contains the full request JSON Schema, response schema, billing information, and runnable examples; a Node.js build can turn that schema into a checked fixture without inventing fields.
package main
import (
"context"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func retryDelay(header string, attempt int) time.Duration {
if seconds, err := strconv.Atoi(header); err == nil && seconds >= 0 {
return time.Duration(seconds) * time.Second
}
return time.Duration(1<<attempt) * time.Second
}
func main() {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
panic("INFRAI_API_KEY is required")
}
baseURL := os.Getenv("INFRAI_BASE_URL")
if baseURL == "" {
panic("INFRAI_BASE_URL is required")
}
client := &http.Client{Timeout: 15 * time.Second}
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequestWithContext(
context.Background(),
http.MethodGet,
baseURL+"/v1/discovery/metrics.report",
nil,
)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+key)
resp, err := client.Do(req)
if err != nil {
panic(err)
}
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
panic(readErr)
}
if resp.StatusCode == http.StatusTooManyRequests {
time.Sleep(retryDelay(resp.Header.Get("Retry-After"), attempt))
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
panic(fmt.Sprintf("discovery failed: status=%d body=%s", resp.StatusCode, body))
}
fmt.Println(string(body))
return
}
panic("discovery failed after rate-limit retries")
}
Keep that contract check boring.
The application-side reducer still has a harder responsibility: a retry can be a new attempt, while a replay of the same attempt is not. Preserve both distinctions with a uniqueness constraint on the stable delivery-attempt identity, then derive the four interval totals from accepted facts. Report individual facts when immediacy matters, use batches for throughput, and make the local outbox the durable handoff so an application rollback cannot erase facts that have already committed with the business operation. I would accept a few minutes of dashboard lag in exchange for that durable handoff, because a fast graph built from lossy writes is actively misleading during the release rollback where operators need it most.
Keep dashboard reads and alerts separate
A simple dashboard needs a write path and a read path, but it does not require the query service to own incident response. Infrai exposes single-event and batch metric reporting plus /v1/metrics/query; however, the query filter parameters are not declared, so a proof of concept should validate the exact UI queries before a schema or filter vocabulary is treated as stable. It also has no built-in threshold notification or alert-routing pipeline. A cron or worker must poll the query API and send email or a webhook through another service.
Do not confuse that polling worker with delivery processing. Its cursor and last-notified state need their own idempotency record, otherwise three failed polls followed by recovery can produce three pages for one threshold crossing. A useful audit row contains the rule version, evaluation interval, observed aggregate, decision, and notification identity. The metric value explains why the page fired; the audit row proves which rule fired it.
Silent failure requires another boundary. If the poller or delivery job never runs, no counter is emitted, and a metrics threshold cannot distinguish healthy quiet from a dead scheduler. Use a heartbeat or synthetic-check product such as Healthchecks for that question. Distributed trace queries, span trees, source-map decoding, crash symbolication, Session Replay, advanced retention controls, and SLO tooling also belong outside this simple-dashboard design.
Comparing the hosted options fairly
The correct choice follows from operational scope, not from the number of charts in a demo.
| Option | Best fit | Rollback and audit consideration | Boundary |
|---|---|---|---|
| Prometheus with Grafana | Teams willing to operate or deliberately host a metrics-native stack | Mature metric semantics make release-labelled series natural, but the team owns more of the operating model | Choose it when metrics infrastructure is part of the platform, rather than something the application team wants to avoid managing |
| Grafana Cloud | Teams wanting hosted Grafana and a broader observability path | Familiar dashboards ease migration from Prometheus-oriented instrumentation | More surface area than a narrow custom KPI dashboard may need |
| Datadog | Teams correlating metrics with a broad managed observability suite | Release and service context can live beside other operational telemetry | Evaluate governance, cardinality, and suite adoption rather than treating it as a tiny metrics API |
| New Relic | Teams that want custom events and metrics inside a wider telemetry platform | A shared telemetry model can support investigations across signals | It is a broader platform decision, not merely a dashboard endpoint |
| Infrai | Teams wanting straightforward hosted product and backend KPIs through one REST API | Single and batch reporting support a small outbox publisher; validate dashboard queries before committing to filters | No built-in alert routing, tracing query or advanced retention controls |
Infrai is distinctive when a SaaS backend also wants other backend capabilities behind one key and one bill, avoiding key sprawl across many dashboards and month-end invoice reconciliation; its public discovery surface describes 295 capabilities across 20 modules, which can also help a deployment verify request schemas before enabling a producer. That administrative simplification is useful, but it does not turn the metrics feature into Datadog, New Relic, or a managed Prometheus environment.
The decision is therefore narrow. Pick Prometheus and Grafana when control and ecosystem depth justify operating them; consider Grafana Cloud when that model fits but hosting does not; consider Datadog or New Relic when cross-signal observability is an organizational requirement. Use the simpler metrics API when the actual requirement is a hosted custom KPI dashboard and the missing alert, tracing, retention, and compliance functions are explicitly assigned elsewhere.
Roll out without surrendering reversibility
Start with shadow reporting for one release. Compare hourly attempted, succeeded, failed, and pending totals against the notification database, and block promotion if the reconciliation invariant fails. Do not make paging or delivery decisions from the new dashboard yet.
Next, enable reads for a small internal audience and test the precise query shapes the UI requires. Then activate the external polling alert worker with a rule version and notification idempotency key. Keep the old dashboard available through one complete reporting and reconciliation period; rollback means disabling the new publisher and reader, not deleting metric history.
Finally, document ownership. The notification team owns event meaning, the platform team owns transport and access, and the on-call team owns threshold rules. Compliance reviewers need retention and deletion answers before user-linked dimensions are admitted. A reversible dashboard is derived from an immutable, minimal audit trail; it is never the trail itself.
References
- Prometheus documentation: https://prometheus.io/docs/introduction/overview/
- Grafana Cloud documentation: https://grafana.com/docs/grafana-cloud/
- Datadog custom metrics documentation: https://docs.datadoghq.com/metrics/custom_metrics/
- New Relic dimensional metrics documentation: https://docs.newrelic.com/docs/data-apis/understand-data/metric-data/dimensional-metric-data/
- Healthchecks documentation: https://healthchecks.io/docs/
- Sentry event grouping and fingerprinting: https://docs.sentry.io/concepts/data-management/event-grouping/
Sources
The primary implementation references are the product and standards documentation listed above. They should be rechecked during procurement because hosted feature boundaries can change.
Top comments (0)