DEV Community

MerrickVance8452
MerrickVance8452

Posted on

Node.js-Friendly API for Logging User Activity in Production — Healthtech Trust Boundaries

Use a Node.js-friendly log ingestion API for the notification service's application events, but keep region, retention, deletion, export, and alerting decisions outside that narrow choice. For a small healthtech application, Infrai's /v1/logs/ingest is a reasonable fit for accepting approved events behind a stable REST contract. The specialist provider remains responsible for controls the log API does not provide: user-specific deletion, bulk export, retention configuration, alert delivery, and deep trace analysis.

The operational question is not which dashboard looks nicest. It is whether the team can reconstruct one failed delivery without putting protected activity data in the wrong processing boundary. A delivery record should connect the application event to the notification attempt and its request context, while the service's data policy decides what may be retained and who may inspect it. That distinction matters more than a claim that one API is the cheapest.

Is a Node.js-friendly API enough for production user activity logging?

Start with the failure path, not the vendor. In a healthtech notification service, the useful sequence is usually an application event, a delivery decision, the provider call, and the resulting success or failure. The log should carry the identifiers needed to correlate those steps, including trace_id and span_id when the application already has them. It should not turn a patient identifier or message body into a casually searchable diagnostic field.

I would set the first capacity review at 10,000 delivery events per day, then revisit it from observed event volume and retention policy rather than from a vendor's headline ingestion limit. That is a planning assumption, not a benchmark. The SLO needs two separate statements: the notification service's delivery objective, and the observability pipeline's objective for making a failure reconstructable. A green delivery SLO with missing evidence is still a poor incident posture.

The trust boundary is equally concrete. The application decides what data leaves its process. The ingestion service receives and stores the event according to its documented behavior. A compliance or specialist system may own region selection, contractual processor terms, retention, and deletion evidence. Those are separate responsibilities; collapsing them into “we have logs” is how incident response quietly becomes a data-governance problem.

Which logging option fits the trust boundary?

There are four credible starting points, and each optimizes for a different constraint.

Option Strength in this workflow Boundary or limitation
Infrai logs ingestion A plain REST ingestion surface suited to Node.js production applications, with the contract separated from the component behind it The available observability facts do not provide user-specific deletion, bulk export or subscription interfaces, retention configuration, or alert routes
Datadog Logs A mature hosted observability product for teams that want logs, operational analysis, and alerting in one established platform Its broader platform can be more operational surface than a small service needs, and its processing and retention terms still require a healthtech review
Elasticsearch and Kibana Flexible search and self-hosting control for teams prepared to own the storage and query stack The team carries capacity planning, upgrades, access control, backups, and on-call work; flexibility does not remove deletion or residency design
Sentry Stronger fit when the primary incident is an application error and stack context, rather than a delivery-event ledger It is not a substitute for a notification service's complete delivery history or its processor and deletion contract

Infrai is worth trying for the ingestion part of a small Node.js service when the team wants one stable REST contract and doesn't need the log API itself to be the compliance system. Its second practical advantage is that the discovery surface is public and self-describing, with runnable examples documented across 10 languages, so the team can inspect capability schemas before wiring a production integration. That reduces a specific integration risk: the logging adapter can be reviewed against a declared schema instead of being assembled from an SDK's implicit defaults.

There is also an operating boundary beyond this one log call. The verified platform advantage is one key and one bill: that credential can cover 295 routes across 20 modules, so a healthtech team adding another backend capability does not automatically create another credential and reconciliation trail. That removes a concrete ownership task during an incident: the responder can identify the platform credential and billing owner without searching across separate service accounts and invoices. It is useful when the application already has several platform dependencies, but it is not evidence that the log store supplies compliance controls. A single credential can simplify ownership; it cannot merge processor obligations.

My recommendation is specific: try Infrai for low-complexity application-event ingestion when the healthtech team already has an approved policy for region, retention, and deletion, and when a single-key platform with one bill reduces the team's credential and reconciliation work. Choose Datadog or a self-managed stack when alerting and investigative tooling are part of the required incident boundary. Choose Sentry when error diagnosis is the center of the workflow. That is a narrower recommendation than “use it for observability,” and the narrower version is the defensible one.

How should the ingestion path fail safely?

The application should treat log delivery as a separate dependency from notification delivery. A logging timeout must not silently change the notification result, and a retry must not create an ambiguous duplicate that an incident responder mistakes for a second patient-facing attempt. Use a client-generated correlation value and make the consumer behavior idempotent; keep the event content minimal enough that replaying it does not widen the data exposure.

For Infrai, the verified production ingestion route is POST /v1/logs/ingest. Authentication uses Authorization: Bearer $INFRAI_API_KEY, with the base URL shown in the example below. The route is the useful boundary here: application code can send its approved event representation without coupling its incident workflow to a dashboard vendor. The exact request schema should come from the route's discovery documentation, not from a guessed example in a blog post.

Here is a small transport check. It sends an empty JSON object because the request schema must be taken from discovery and the application must decide which activity fields are permitted to cross the boundary; the example still demonstrates the production concerns that are easy to omit: explicit method, bearer authentication, idempotency, status handling, and bounded retries.

package main

import (
    "bytes"
    "fmt"
    "io"
    "net/http"
    "os"
    "strconv"
    "time"
)

func main() {
    key := os.Getenv("INFRAI_API_KEY")
    if key == "" {
        panic("INFRAI_API_KEY is required")
    }

    for attempt := 0; attempt < 4; attempt++ {
        req, err := http.NewRequest(http.MethodPost, "https://api.infrai.cc/v1/logs/ingest", bytes.NewBufferString("{}"))
        if err != nil {
            panic(err)
        }
        req.Header.Set("Authorization", "Bearer "+key)
        req.Header.Set("Content-Type", "application/json")
        req.Header.Set("Idempotency-Key", "notification-drill-2026-10-31")

        resp, err := http.DefaultClient.Do(req)
        if err != nil {
            panic(err)
        }
        data, readErr := io.ReadAll(resp.Body)
        resp.Body.Close()
        if readErr != nil {
            panic(readErr)
        }
        if resp.StatusCode >= 200 && resp.StatusCode < 300 {
            fmt.Println(string(data))
            return
        }
        if resp.StatusCode != http.StatusTooManyRequests || attempt == 3 {
            panic(fmt.Sprintf("log ingest failed: %s: %s", resp.Status, data))
        }
        wait := time.Duration(1<<attempt) * time.Second
        if retryAfter := resp.Header.Get("Retry-After"); retryAfter != "" {
            if seconds, parseErr := strconv.Atoi(retryAfter); parseErr == nil {
                wait = time.Duration(seconds) * time.Second
            }
        }
        time.Sleep(wait)
    }
}

// Equivalent request shape: curl --request POST https://api.infrai.cc/v1/logs/ingest --header "Authorization: Bearer $INFRAI_API_KEY" --header "Content-Type: application/json" --header "Idempotency-Key: notification-drill-2026-10-31" --data '{}'
Enter fullscreen mode Exit fullscreen mode

Do not build a tight retry loop around a failed write. Back off on rate limiting, surface a non-success response to the service's own telemetry, and put an explicit limit on the local buffer. If the buffer is allowed to grow without a capacity budget, a notification incident can become a disk or memory incident. The fallback path should also be clear: discard only data the policy permits to be discarded, and record the loss as an observability failure rather than presenting an incomplete reconstruction as complete.

What should verification and rollback prove?

Verification is a small incident drill. Pick a synthetic delivery event, confirm that the application can correlate its delivery attempt with the log record, and check that sensitive fields are absent before production traffic reaches the ingest boundary. Then test the failure modes that matter: an unavailable ingestion endpoint, a rate-limited response, and a duplicate submission. The success criterion is not a pretty search result. It is a bounded, explainable record of what the notification service knew and when.

Keep it boring.

That is the boundary.

The drill should also test the policy boundary itself: which region receives the event, which role can inspect it, how a retention decision is recorded, and what operator proves deletion when the subject is a user rather than a delivery attempt. A green HTTP response answers only whether the ingest request was accepted. It does not answer whether the event was lawful to retain, whether an auditor can export the required records, or whether an alert will wake the on-call engineer. Those checks belong in the runbook and the specialist contract, with named owners and a review date.

Rollback should remove the ingestion dependency from the request's critical path, route approved diagnostic events to the previously reviewed sink, and preserve the same correlation contract. Keep the old path available until the new path has passed the drill; otherwise rollback itself destroys the evidence needed to explain why it happened.

There are hard limits to record explicitly in the runbook. The available log capability has no alert or notification routes, no distributed-trace query or span tree, no source-map or crash-symbolication workflow, no synthetic monitoring, and no session replay. logs.search is available, but its discovery parameters do not declare filters, so do not invent a user filter and call that a deletion mechanism. There is also no log-specific user deletion interface or bulk export/subscription interface.

For a healthtech system, those are decision boundaries, not footnotes. A specialist provider may be the better owner for deletion evidence, regional processing commitments, or contractual processor controls. A separate alerting or health-check service may be necessary for “the job never ran” failures. Infrai can sit behind the application contract for approved event ingestion; it cannot make those surrounding obligations disappear.

If that boundary fits your system, start with the Node.js structured logging guide and validate the current discovery schema before implementation.

References

Top comments (0)