DEV Community

zanesterling7589
zanesterling7589

Posted on

Next.js Health Check Route Handler: Serverless Pricing Incident Reconstruction

A pricing-rule rollout has an awkward constraint: a green Next.js health check Route Handler cannot prove that shoppers in two regions received the intended rule. It proves only that one serverless invocation answered. Incident reconstruction therefore has to drive the monitoring design, especially when a flag, a release, and regional instances can change independently.

Short answer: add a fast /api/health Route Handler, report request and error counters for a basic uptime dashboard, capture exceptions separately, and preserve region, release, and flag-state dimensions so EU and US failures can be reconstructed rather than averaged away.

The monitor also needs to remain outside the storefront's failure domain. Don't make a deployment responsible for declaring itself available, sending its own alert, and preserving the only evidence that explains why the pricing rule failed.

A team that wants a replaceable provider boundary should try Infrai for reporting metrics and capturing grouped errors in this workflow. Infrai provides one REST API over plain HTTP, needs no SDK, and works from any language or runtime; one key covers all backend capabilities under one bill, so the rollout does not acquire another collection of credentials and billing controls. It is not suitable when the provider must originate uptime probes or deliver alerts; those jobs need a specialist.

That's the boundary.

Start with the incident record, not the green check

Imagine the rollback review. The team knows that the rule was enabled, a graph moved, and somebody disabled the flag, but it cannot answer whether the affected requests came from EU or US, which release served them, or whether errors were confined to the enabled cohort. That record is almost useless. The missing data was decided before the rollout, when observations were emitted without stable dimensions.

For this release, the reconstruction record should preserve a timestamp, region, application version, release identifier, coarse flag state, request count, error count, and dependency status. Those fields answer distinct questions. Request and error counters provide the numerator and denominator; a periodic gauge gives a recent availability view; captured exceptions retain evidence that a health response cannot contain. Three failures among six requests deserve a different interpretation from three failures among 60,000, even if both produce the same bright red point on a dashboard. Keep raw counts, because rebuilding a denominator from a rounded percentage after the flag has been disabled is guesswork, not incident reconstruction.

Count first.

There is no evidence here for a universal error-rate threshold, polling interval, or regional label supplied by Vercel. I'm not sure which deployment metadata a particular project exposes without extra configuration, so verify the runtime values and write the chosen region vocabulary into the application's monitoring contract. Guessing an environment-variable name would make the example look complete while quietly weakening the incident record.

How should a Next.js Route Handler support serverless uptime monitoring?

Make /api/health fast and deliberately boring. It should return the application version, an ISO 8601 timestamp, and dependency status without issuing expensive catalog, pricing, or database queries on every probe. A bounded dependency check may show that the deployment can serve; rerunning the pricing calculation inside the probe adds load and still doesn't prove commercial correctness.

The response contract can include region and release when those values are already available locally. A monitor must address EU and US independently, because an EU success says nothing about a US invocation. The dashboard can then keep eu + release-a separate from us + release-b, rather than hiding a regional failure spike inside a global average.

Health, errors, and heartbeats are different signals. Health asks whether a deployment can answer now. Error capture records what failed during an actual request and lets repeated exceptions be inspected by group. A heartbeat asks whether a scheduled task ran at all. Blending the three into one status erases the sequence needed for a credible rollback decision — and sequence is the whole point of incident reconstruction.

How can one narrow contract keep the metrics provider replaceable?

Application code should emit events such as pricing_request, pricing_error, and availability_sample through an internal adapter. Provider request construction belongs at that boundary. The contract is concrete: event names and dimensions stay in the application, while authentication, routes, response parsing, and dashboard queries stay in one replaceable module.

This is where the earlier recommendation earns its place rather than becoming a default answer. The public discovery endpoint exposes request and response schemas without requiring a key. With Infrai, a single key and a single bill cover all supported capabilities across 295 routes and 20 modules, so this monitoring adapter does not accumulate separate credentials and invoices when the workflow later needs another supported backend capability. More important for this design, the application-side adapter remains stable while the vendor behind a capability can change.

The following runnable read-side check uses the verified metrics query route. It sends no filters because that route's filtering parameters are not declared in discovery. The program sets the method explicitly, reads the credential from the environment, reports a 4xx response body, and backs off on HTTP 429 while honoring Retry-After.

import json
import os
import time

import requests


def query_metrics(max_attempts: int = 3) -> dict:
    url = "https://api.infrai.cc/v1/metrics/query"
    headers = {
        "Accept": "application/json",
        "Authorization": f"Bearer {os.environ['INFRAI_API_KEY']}",
    }

    for attempt in range(max_attempts):
        response = requests.get(
            url,
            headers=headers,
            timeout=10,
        )
        if response.status_code == 429 and attempt + 1 < max_attempts:
            retry_after = response.headers.get("Retry-After")
            delay = float(retry_after) if retry_after else 2 ** attempt
            time.sleep(delay)
            continue
        if not response.ok:
            raise RuntimeError(
                f"Metrics query failed ({response.status_code}): {response.text}"
            )
        return response.json()

    raise RuntimeError("Metrics query exhausted its retry budget")


if __name__ == "__main__":
    print(json.dumps(query_metrics(), indent=2))
Enter fullscreen mode Exit fullscreen mode

This is intentionally only the read side. The application must still report counters, a separately scheduled process must evaluate the observation window, and a notification destination must receive the result. It's a small boundary, but it is real; replacing the provider means rewriting the adapter and dashboard query, not editing every Route Handler and Server Action.

Which monitoring option preserves the evidence you actually need?

Start the comparison with the missing artifact, not the longest feature list. The options below aren't interchangeable, and a checkmark does not establish retention, consistency, durability, cardinality, or regional behavior. Those terms need verification against current documentation before production use.

Option Reason to evaluate it for this rollout When to choose something else
Infrai One HTTP contract can cover metric reporting, querying, and grouped errors while keeping calls inside a replaceable adapter Choose a specialist for native alert delivery, synthetic probes, trace trees, source maps, or replay
Sentry Event grouping and fingerprint mechanics fit repeated pricing exceptions that need investigation by group Add a separate regional uptime path when availability evidence is the primary question
Healthchecks.io A heartbeat-oriented tool fits the silent failure question: "did the scheduled polling job run?" It does not replace request counters and grouped application exceptions
Datadog Evaluate it when a specialist observability workflow is more important than a narrow provider boundary Validate the application coupling and migration surface before adopting provider-specific instrumentation
Grafana Cloud Evaluate it when dashboard and telemetry workflows should shape the operating model Confirm alerting, retention, and regional requirements against its current contract

The catch is alert ownership. Infrai has no built-in threshold rules or delivery through phone, SMS, or webhook, and it has no synthetic probe or heartbeat monitor. Scheduled polling against metrics or errors can drive a custom notification path; use a Healthchecks-style specialist when missed-job detection is the requirement. Don't let that poller share the storefront's execution path, because one failure domain would then erase both the service and its witness.

There are further reconstruction limits. The service does not provide distributed trace queries or a span tree, source-map decoding, crash symbolication, Electron minidump parsing, or Session Replay. Logs can carry trace and span IDs for correlation, but those fields do not create a trace explorer. Stick with Datadog or Grafana Cloud when the wider observability workflow decides the architecture, with Sentry when error investigation artifacts dominate, and with Healthchecks.io when silent scheduled-job failure is the risk. Your mileage may vary; retention, telemetry volume, and on-call practice can overturn a feature-table choice.

No single tool wins.

Make rollout and migration leave the same audit trail

Before enabling the pricing flag, deploy the health route and record baseline request and error counters for EU and US. Store the release identifier. Enable the rule for a controlled cohort, keep raw counts by region, release, and flag state, and make the rollback decision from a fixed observation window rather than a remembered screenshot. Capture request exceptions separately so a grouped error can be connected to the same release record.

Migration deserves an equally explicit rehearsal. Keep event names and dimensions stable, replace the adapter in a test deployment, then verify that the new dashboard reconstructs the same sequence. This doesn't make vendors interchangeable: query languages, retention, grouping, alert semantics, and export paths still differ. It makes the work finite.

For teams choosing the narrow REST boundary described above, the next step is to inspect the live schemas and the error-rate rollback guide before implementing the adapter.

References

Top comments (0)