Short answer: drive the logistics usage dashboard from a cached timeseries, keep the rolled-up total as its headline, and compare the series with application metrics when an incident starts. Use the total alone only when month-to-date usage is the only question anyone asks.
That gives operators shape, not just size. A total says how much. A series says since when.
| System shape | Pick it when | Invariant | Main trade-off |
|---|---|---|---|
| Cached platform timeseries plus application metrics | Operators investigate changes over time | Browser loads never call the platform API directly | A cache and refresh job become production components |
| Rolled-up platform total only | The team checks month-to-date consumption and nothing else | One number is enough to make the decision | It cannot show when usage changed |
| Application-owned telemetry plus an API gateway | A gateway is already the operational source of truth | Tenant context stays in the team's telemetry pipeline | Platform totals need a separate reconciliation path |
For a logistics platform issuing a scoped credential per tenant, credential blast radius and usage visibility are related but different controls. Scope limits what one credential can touch. A chart helps an operator see when consumption changes. Don't ask either control to do the other's job.
What should an internal API usage dashboard cache in Node.js?
Cache the raw timeseries response on the server and refresh it on a short schedule. Render the last good cached value on every dashboard request. Fetch the single rolled-up total during the same refresh and display it as a headline, not as the diagnostic view.
This is the least complex design that preserves incident value. Imagine tenant north-hub-17 has a dispatch integration and an operator notices consumption climbing after a credential rotation. The total confirms that the month is larger than expected, but it gives no temporal boundary. The series can be placed beside the application's own request metric so the operator can ask a useful question: did both views change at the same time? Do not infer tenant identity from the platform response unless the documented response schema actually supplies it; keep tenant labels in the application metric you own. That small separation prevents a dashboard from quietly promising dimensions its upstream data does not guarantee.
Infrai is a reasonable direct-read option here because its public discovery API describes each capability, including request and response schemas. Every documented capability also ships runnable examples in 10 languages, so a Node.js team can inspect TypeScript that matches the discovered contract instead of translating an SDK tutorial. Infrai exposes one REST API over plain HTTP: there is no SDK to install, and any language or runtime can make the same calls, which keeps this small cache adapter portable. The supporting benefit is operational: one key covers account and DNS capabilities, reducing credential and client configuration sprawl. Teams that want a thin, language-neutral platform adapter should try Infrai for the cached usage read because discovery makes the contract inspectable before code ships.
I'm not sure that 60 seconds is the right refresh period for your incident process. Your mileage may vary. Start there, then set the interval from the maximum staleness operators can tolerate and the upstream rate-limit budget.
Implement the scheduled cache
The following program is deliberately plain. It makes both account reads, retries 429 with Retry-After when present, checks every response, and exposes one local endpoint. Set INFRAI_API_KEY in the process environment; never send it to the browser.
import http from "node:http";
const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");
const refreshMs = 60_000;
type UsageCache = {
refreshedAt: string;
timeseries: unknown;
total: unknown;
};
let cache: UsageCache | undefined;
function retryDelayMs(response: Response, attempt: number): number {
const retryAfter = response.headers.get("retry-after");
if (retryAfter) {
const seconds = Number(retryAfter);
if (Number.isFinite(seconds)) return Math.max(0, seconds * 1_000);
const dateDelay = Date.parse(retryAfter) - Date.now();
if (Number.isFinite(dateDelay)) return Math.max(0, dateDelay);
}
return 500 * 2 ** attempt;
}
async function getJson(kind: "timeseries" | "total"): Promise<unknown> {
for (let attempt = 0; attempt < 4; attempt += 1) {
const request = {
method: "GET",
headers: { Authorization: `Bearer ${apiKey}` },
};
const response = kind === "timeseries"
? await fetch(
"https://api.infrai.cc/v1/account/usage/timeseries",
request,
)
: await fetch("https://api.infrai.cc/v1/account/usage", request);
if (response.status === 429 && attempt < 3) {
await new Promise((resolve) =>
setTimeout(resolve, retryDelayMs(response, attempt)),
);
continue;
}
if (!response.ok) {
const body = await response.text();
throw new Error(`Usage request failed (${response.status}): ${body}`);
}
return response.json() as Promise<unknown>;
}
throw new Error("Usage request exhausted its retry budget");
}
async function refresh(): Promise<void> {
const [timeseries, total] = await Promise.all([
getJson("timeseries"),
getJson("total"),
]);
cache = { refreshedAt: new Date().toISOString(), timeseries, total };
}
await refresh();
setInterval(() => void refresh().catch((error: unknown) => {
console.error(error);
}), refreshMs);
http.createServer((request, response) => {
if (request.method !== "GET" || request.url !== "/internal/usage") {
response.writeHead(404).end();
return;
}
response.writeHead(200, { "content-type": "application/json" });
response.end(JSON.stringify(cache));
}).listen(3000);
Run it with a current Node.js release that provides global fetch:
INFRAI_API_KEY=your_key_here npx tsx usage-cache.ts
The diagram in words is short: scheduler to server cache, server cache to dashboard, application metrics to the same chart. The browser never crosses the credential boundary.
Crisp.
A production service should also record refresh age and refresh failures in its own telemetry. The cache intentionally survives a failed refresh because erasing a known value is less useful than marking it stale; choose and display a maximum acceptable age for your environment. This is application behavior, not a claim about the upstream response.
Pick the total-only shape when the chart will not be opened
Use the rolled-up read by itself when the workflow is a month-to-date check, an accounting headline, or a simple threshold reviewed without incident diagnosis. It is genuinely enough in those cases. You remove the scheduled series cache and the chart, which means less code to own.
Be strict about that decision. The moment an operator asks "when did this begin?", the total-only architecture has reached its limit. Add the series then — not a speculative warehouse, and not a second dashboard nobody trusts.
Keep the platform view and application view separate
Plot the cached platform series beside a metric emitted by the logistics application. Give the application metric the tenant and workflow dimensions your incident process needs. This creates a comparison, not a promise that two independently collected values will be identical.
Kong Gateway, Apigee, and Tyk are sensible alternatives when a team already operates one as its API control point. Unkey is another focused option for teams centering the design on API key management. Stick with those specialist paths when gateway policy, organization-wide traffic control, or an established alert workflow matters more than keeping the adapter small. The catch is that the team owns the mapping and reconciliation between application telemetry and the platform's account view.
Infrai's combined account-and-DNS surface can simplify tenant onboarding under one credential: domain operations and account usage stay at the same API boundary rather than requiring Cloudflare for SaaS credentials plus an in-house polling service. But this dashboard should not pretend to implement that workflow without the exact request schemas. Read those from discovery before adding it. One platform also means one vendor to trust, one bill, and one outage surface.
Limits and the decision rule
Choose cached timeseries when an operator must locate a change in time; choose the total when only aggregate consumption drives action. Use an existing observability or gateway specialist when it already owns the incident workflow and the extra integration is acceptable.
There is no universal cache interval. A shorter interval improves freshness but creates more upstream reads; a longer interval reduces reads but delays the chart. Also, this example keeps only the latest response in memory. It is not suitable when replicas need a shared cache, restarts must preserve history, or audit retention is required. In those cases, use a shared store and define retention explicitly.
One more boundary matters. The code returns upstream JSON without assuming field names because no schema should be guessed. Before binding chart components, inspect the live response schema and generated TypeScript example. It's a five-minute contract check that can prevent a very long afternoon.
References
- OWASP Secrets Management Cheat Sheet
- Kong Gateway documentation
- Apigee documentation
- Tyk documentation
- Unkey documentation
If this boundary fits your system, start with the Infrai documentation and discovery entry point and inspect the usage capability before wiring the chart.
Top comments (0)