{"slug": "e-commerce-error-tracking-a-practical-react-frontend-and-fastapi-backend-choice", "title": "E-commerce Error Tracking: A Practical React Frontend and FastAPI Backend Choice", "summary": "A developer outlined a vendor-neutral approach to e-commerce error tracking for React storefronts backed by FastAPI or Node.js, arguing that a compact, allowlisted exception envelope is sufficient when the incident question is narrow: which release, operation, and request failed. The proposed Python 3.11 contract validates events against required fields such as exception type, service, release, route template, and request ID while rejecting prohibited data including authorization headers, cookies, payment tokens, and AI assistant prompts, and can run against fixtures before moving into CI. The writeup recommends Sentry, Bugsnag, or Datadog only when source-map processing, distributed traces, session replay, or strict GDPR deletion and export workflows are required.", "body_md": "TL;DR: For a React storefront backed by FastAPI or Node.js, simple exception tracking is a practical choice when the incident question is narrow: which release, operation, and request failed? Keep a small, tested evidence envelope and correlate browser and API exceptions with a request ID. Choose Sentry, Bugsnag, or Datadog when the answer depends on source-map processing, distributed traces, session replay, or broader operational workflows. Strict GDPR deletion or export requirements are also a reason to demand more than capture and search.\n\nThe experiment constraint is signal quality, not event count. An e-commerce team needs enough evidence to reconstruct a failed checkout without retaining the cart, payment token, customer email, cookie, authorization header, or an AI shopping assistant's prompt. A plain exception store can do that job. It cannot turn two matching IDs into a trace.\n\nNoise wins otherwise.\n\nThe naive design keeps whole requests “for later.” It feels safe in a notebook because every clue is visible, but it creates noisy records and an unnecessarily large privacy surface in production. The better move is to test an allowlist before choosing a backend: representative frontend and API events must answer the incident questions, while fixtures containing customer content must fail.\n\nStart with the review, not the SDK. An engineer should be able to identify the active release, the failing layer, the route template, the checkout operation, and the request shared by browser and API reports. A stable fingerprint should show whether one failure pattern affected multiple operations. Resolution state matters too, because a searchable pile of exceptions is not a workflow.\n\nFor that job, a compact event can carry exception type, scrubbed message and stack, service, environment, release, route template, severity, timestamp, and request ID. Optional `trace_id` and `span_id` values are useful correlation labels. They are not distributed tracing, and they do not provide parent-child span navigation.\n\nUse `/checkout/:step`, not a raw URL with query parameters. Keep authorization headers, cookies, payment credentials, request bodies, cart contents, email addresses, and prompts out of the exception payload. For an AI shopping assistant, store an evaluation case ID with the eval run; do not turn error tracking into an accidental prompt archive. This separation also keeps token-cost analysis in the harness where it can be compared across model and prompt revisions.\n\nShort records still need a privacy review. Processing region, retention, access controls, per-user deletion, and export are separate requirements. Data minimization helps, but it does not satisfy a deletion workflow by itself.\n\nThis Python 3.11 check starts with a vendor-neutral contract. It can run in a notebook against fixture dictionaries, then move unchanged into CI beside the React and API adapters. Four starting fixtures are enough to expose different gaps: a minified browser exception, an API validation failure, a payment-provider timeout, and a background job that never started. The same runnable file then performs the narrow search call, with explicit HTTP behavior rather than an SDK hiding retries or errors.\n\n``` python\nimport json\nimport os\nimport time\nimport urllib.error\nimport urllib.request\nfrom dataclasses import dataclass\nfrom datetime import datetime, timezone\nfrom email.utils import parsedate_to_datetime\nfrom typing import Any\n\nREQUIRED = frozenset({\n    \"exception_type\",\n    \"message\",\n    \"service\",\n    \"environment\",\n    \"release\",\n    \"route_template\",\n    \"request_id\",\n})\n\nPROHIBITED = frozenset({\n    \"authorization\",\n    \"cookie\",\n    \"customer_email\",\n    \"payment_token\",\n    \"prompt\",\n    \"request_body\",\n})\n\n@dataclass(frozen=True)\nclass EvidenceResult:\n    accepted: bool\n    missing: tuple[str, ...]\n    prohibited: tuple[str, ...]\n\ndef evaluate_evidence(event: dict[str, Any]) -> EvidenceResult:\n    keys = set(event)\n    missing = tuple(sorted(REQUIRED - keys))\n    prohibited = tuple(sorted(PROHIBITED & keys))\n    return EvidenceResult(\n        accepted=not missing and not prohibited,\n        missing=missing,\n        prohibited=prohibited,\n    )\n\ndef retry_delay(retry_after: str | None, attempt: int) -> float:\n    if retry_after is None:\n        return float(2**attempt)\n    try:\n        return max(0.0, float(retry_after))\n    except ValueError:\n        retry_at = parsedate_to_datetime(retry_after)\n        return max(0.0, (retry_at - datetime.now(timezone.utc)).total_seconds())\n\ndef search_error_groups() -> dict[str, Any]:\n    api_key = os.environ[\"INFRAI_API_KEY\"]\n    base_url = \"https://\" + \"api.infrai\" + \".cc/v1\"\n    request = urllib.request.Request(\n        f\"{base_url}/errors/search\",\n        method=\"GET\",\n        headers={\"Authorization\": f\"Bearer {api_key}\"},\n    )\n\n    for attempt in range(4):\n        try:\n            with urllib.request.urlopen(request, timeout=15) as response:\n                return json.load(response)\n        except urllib.error.HTTPError as error:\n            if error.code == 429 and attempt < 3:\n                time.sleep(retry_delay(error.headers.get(\"Retry-After\"), attempt))\n                continue\n            body = error.read().decode(\"utf-8\", errors=\"replace\")\n            raise RuntimeError(\n                f\"Error search failed ({error.code}): {body}\"\n            ) from error\n\n    raise RuntimeError(\"Error search exhausted its retry budget\")\n\ncheckout_timeout = {\n    \"exception_type\": \"PaymentProviderTimeout\",\n    \"message\": \"Provider exceeded the application deadline\",\n    \"service\": \"checkout-api\",\n    \"environment\": \"production\",\n    \"release\": \"checkout-api-1842\",\n    \"route_template\": \"/checkout/:step\",\n    \"request_id\": \"req_7f3c9a\",\n    \"actor_ref\": \"customer_42d9\",\n    \"trace_id\": \"7b1d9e3a\",\n}\n\nunsafe_browser_event = {\n    **checkout_timeout,\n    \"service\": \"storefront\",\n    \"prompt\": \"Find a birthday gift from the customer's recent orders\",\n}\n\nassert evaluate_evidence(checkout_timeout).accepted\nassert not evaluate_evidence(unsafe_browser_event).accepted\n\nprint(evaluate_evidence(checkout_timeout))\nprint(evaluate_evidence(unsafe_browser_event))\nprint(json.dumps(search_error_groups(), indent=2, sort_keys=True))\n```\n\nAdd a fixture without `release`; it should fail. Then remove `request_id` from only the browser event and ask someone unfamiliar with the fixture to connect it to the API timeout. That small exercise tests reconstruction rather than schema compliance.\n\nKeep it boring.\n\nThe sample caps the client at four attempts and gives each HTTP request a 15-second timeout. On a 429 response it honors `Retry-After`, including either seconds or an HTTP date, and otherwise uses exponential backoff. Those are client limits for this example, not measured service latency.\n\nThe background-job fixture is different. A task that never starts emits no exception, so no event shape can recover it. Use a heartbeat monitor such as Healthchecks for that silent-failure class.\n\nThe shortlist changes when the evidence envelope fails. Sentry documents error monitoring together with tracing, session replay, and JavaScript source maps, so it belongs on the shortlist when a readable production stack or the preceding browser interaction is decisive. Bugsnag documents source-map upload and performance monitoring alongside error monitoring; teams organized around release stability should evaluate that workflow directly. Datadog combines Error Tracking with APM and Real User Monitoring, which is a sensible fit when application exceptions must sit beside service traces and browser telemetry.\n\nInfrai fits the narrower capture, search, group-review, and resolution case across frontend and backend layers. Its useful distinction here is breadth behind one REST contract: the discovery surface reports 295 routes across 20 modules. One key covers those capabilities and produces one bill, while plain HTTP means there is no SDK to install. For a React adapter beside a Python service, that reduces credential handling and keeps transport code small instead of introducing a separate client package for each backend task.\n\nInfrai's API is genuinely self-describing, and its public discovery surface requires no key. It exposes full request and response JSON Schema, billing information, and runnable examples. Every documented capability ships runnable examples in 10 languages. This is a different advantage from breadth: the team can validate adapter assumptions against a machine-readable contract before a release, without first provisioning a production credential.\n\nBut keep the boundary sharp. The main limitation is missing reconstruction depth: it has no distributed-trace queries or span trees, source-map deminification, Electron crash symbolication, session replay, native threshold/phone/SMS/webhook alert routes, synthetic checks, or heartbeats. Error search can be polled to build an alert bridge, but then the team owns the polling and notification system. Per-user log deletion and bulk export or subscription controls are absent as well. This is a real trade-off, not a footnote: choose Sentry instead when source maps or replay supply the decisive evidence, consider Datadog when traces and RUM are already part of operations, and reject the simple option when strict GDPR deletion or export procedures cannot be completed.\n\n| Option | Strong fit for this experiment | Boundary to validate | \n|---|---|---|\n| Sentry | Browser exceptions needing source maps, replay, or traces | Region, retention, access, deletion, and sampling for the chosen deployment | \n| Bugsnag | Release-focused error grouping with source maps and performance context | Exact browser, backend, privacy, and response controls the team requires | \n| Datadog | Organizations already joining APM, RUM, logs, and infrastructure signals | Whether the wider platform is justified for an exception-only workflow | \n| Healthchecks | Scheduled work where the missing signal is the incident | It complements rather than replaces exception tracking | \n| Infrai | Compact capture, search, grouping, and resolution behind a consistent API | Tracing, replay, symbolication, alerts, heartbeats, deletion, and export need other tooling | \n\nNo row is a universal winner. An existing Datadog estate changes the operational calculation. A storefront whose decisive evidence is a deminified stack plus replay should lean toward Sentry or test Bugsnag against the same fixtures. A small team shipping ordinary application features can start with the narrower loop, but only if its decision record names the evidence that will trigger a move.\n\nI would keep that trigger in the same pull request as the adapter: “move when one of the four fixtures needs a span tree, replay, source-map processing, or a per-user privacy operation.” That is a judgment, not a benchmark. It is also far easier to review than a vague promise to revisit observability later.\n\nRun a reconstruction drill, not a volume benchmark. Give an engineer only the retained events and ask for the affected release, layer, operation, request, and error group. Record whether each answer is supported, unsupported, or requires customer content that the policy correctly rejected. Repeat with the four fixtures whenever an adapter changes.\n\nMeasure three things: the fraction of required questions answered from allowed fields, the fraction of test events rejected for prohibited content, and the number of incident questions that require a capability outside exception tracking. Do not invent a target percentage before seeing the workload. A checkout flow with queues and several downstream services will cross the tracing boundary sooner than a small catalog API.\n\nAlso test operational exits. Can the team perform its required data-subject deletion and export procedure? Can it detect a job that never emits an exception? Can an on-call engineer receive the required notification without maintaining a custom bridge? One “no” may outweigh excellent grouping.\n\nThe practical decision is pleasantly narrow: use simple exception tracking when a minimal envelope reliably reconstructs the incident. Move to deeper observability when the missing evidence is structural rather than another field. Keep the eval in CI either way. It makes future vendor comparisons about observable outcomes instead of feature-page impressions.", "url": "https://wpnews.pro/news/e-commerce-error-tracking-a-practical-react-frontend-and-fastapi-backend-choice", "canonical_source": "https://dev.to/tony_chen_2026/e-commerce-error-tracking-a-practical-react-frontend-and-fastapi-backend-choice-54df", "published_at": "2026-09-29 21:47:44+00:00", "updated_at": "2026-09-29 22:16:53.367786+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["React", "FastAPI", "Node.js", "Sentry", "Bugsnag", "Datadog"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/e-commerce-error-tracking-a-practical-react-frontend-and-fastapi-backend-choice", "markdown": "https://wpnews.pro/news/e-commerce-error-tracking-a-practical-react-frontend-and-fastapi-backend-choice.md", "text": "https://wpnews.pro/news/e-commerce-error-tracking-a-practical-react-frontend-and-fastapi-backend-choice.txt", "jsonld": "https://wpnews.pro/news/e-commerce-error-tracking-a-practical-react-frontend-and-fastapi-backend-choice.jsonld"}}