Skip to content

Repository files navigation

ddog

A sample app that uses React v18 and node >= 24.13.0 to show an interactive UI and send telemetry and errors to datadog.


The rest of this doc was generated by an LLM.

Implementation

Quick start

bin/dev/start     # installs deps + seeds .env if needed, then runs both servers

Vite serves the app on :5173 and the BFF listens on :8787. The script resolves Node through mise so the pinned 24.20.0 is used regardless of what is on PATH.

bin/dev/start --web      # Vite only
bin/dev/start --bff      # BFF only
bin/dev/start --agent    # also `docker compose up -d` the Datadog agent first

bin/dev/stop             # both servers and the agent container
bin/dev/stop --servers   # leave the container running
bin/dev/stop --agent     # only the container

bin/dev/test             # typecheck, then vitest with coverage, then Playwright
bin/dev/test --unit      # or --e2e / --types / --watch

stop only touches processes whose working directory is this project, so an unrelated program holding :5173 or :8787 is reported and left alone. It is safe to run when nothing is up.

All three take --help. The underlying npm scripts (npm run dev, npm test, …) still work if you prefer them.

Pick Mock in the UI and start checking items — captured DogStatsD lines appear in the inspector at the bottom of the page. The selected mode is remembered across reloads, so app.started is observable too.

How the three modes work

Browsers can't open UDP sockets, so nc -u -l 8125 and dd-trace can only work server-side. Telemetry therefore has two paths, and the radio buttons pick which (if either) is used:

Mode Browser BFF (:8787) Agent
Off no-op transport
Mock captured in memory, rendered as DogStatsD lines, mirrored to console.debug
Agent Datadog RUM (sessions + error stacks) POST /api/telemetryhot-shots DogStatsD UDP :8125 + APM :8126

src/telemetry/ holds the client and its transports; server/ holds the BFF. Both render metrics under one ddog.* namespace so Mock output and real agent traffic are directly comparable.

Events

Event Fires when Notable tags
ddog.app.started app mounts
ddog.tab.changed tab switches from, to
ddog.item.checked / .unchecked checkbox toggles item_name, category, price, cart_total
ddog.cart.limit_exceeded cart total passes $25 cart_total, limit, item_count

The over-$25 case throws a real CartLimitExceededError, which is reported and then caught so the cart stays usable — un-check something and the banner clears.

Capturing without an agent

Mock mode is in-page, but the BFF's real UDP output can be sniffed directly:

npm run dd:sniff                # nc -u -l 8125
npm run dev:bff                 # in another shell, then use Agent mode
ddog.item.checked:1|c|#service:ddog,env:development,category:Fruit,price:5
ddog.cart.limit_exceeded:1|c|#service:ddog,env:development,limit:25
ddog.errors:1|c|#service:ddog,env:development,error_type:CartLimitExceededError

DD_TRACE_DEBUG=true dumps dd-trace spans to the console — the server-side equivalent of Mock mode.

Sending to Datadog

Agent mode has two independent halves. They need different credentials, and one of them works today.

1. Browser RUM — working

.env already holds the RUM application ID and client token, so RUM ships straight from the browser to browser-intake-datadoghq.com. No agent, no API key, and no Docker required for this half. Just:

npm run dev     # pick "Agent" in the UI

Verified against the live intake — a session sends custom actions and the error with full context:

[action] tab.changed    ctx {"from":"Fruit","to":"Veggies","value":1}
[action] item.checked   ctx {"item_name":"Cherries","price":"10","cart_total":"10"}
[error]  Cart total $29 exceeds the $25 limit
         ctx {"limit":"25","cart_total":"29","item_count":"3","metric":"cart.limit_exceeded"}

tagged service:ddog-web, version:0.1.0 (injected from package.json), application.id:28da096a-…. The error appears in Error Tracking; the actions in RUM → Explorer.

@datadog/browser-rum-react's reactPlugin({ router: false }) is registered so errors are attributed to React components. router: false because this app has no router.

Finding the data in the Datadog UI

Selecting Agent reveals a "View in Datadog" panel with deep links, built from .env so they carry the right site and application id. It is the counterpart to Mock mode's inspector: Mock shows what was captured locally, these show where the data actually went.

Link Shows
RUM Explorer @type:action — tab changes, checks, un-checks
Error Tracking @error.type:CartLimitExceededError
Session Replay recorded sessions, 20% sampled
Metrics metric summary filtered to ddog.
APM Service latency and errors for the ddog BFF
APM Traces individual POST /api/telemetry spans

src/telemetry/dd-links.ts maps the intake site to the matching UI host — datadoghq.com/.eu take an app. prefix, while regional sites (us3., us5., ap1.) are already UI hosts.

2. BFF metrics via the agent — working

DD_API_KEY is set in .env, so the agent forwards ddog.* metrics and APM traces to the account.

npm run dd:up     # docker compose up -d — agent on :8125/udp and :8126
npm run dev       # BFF reads .env via node --env-file-if-exists
bin/dev/stop      # stop the servers and the agent when finished

Verified against the live agent. agent dogstatsd-stats after one session:

ddog.tab.changed          | env:development service:ddog from:Ice_Cream to:Fruit  | 1
ddog.item.checked         | env:development service:ddog item_name:Cherries price:10 cart_total:10 | 1
ddog.cart.limit_exceeded  | env:development service:ddog limit:25 cart_total:29 item_count:3       | 1
ddog.errors               | env:development service:ddog error_type:CartLimitExceededError         | 1

The forwarder reported 18 series_v3 submissions with 0 dropped, 0 retried and 0 HTTP errors, and the trace agent received spans from dd-trace for the POST /api/telemetry request.

DD_DOGSTATSD_METRICS_STATS_ENABLE is on in docker-compose.yml — that is what makes docker exec ddog-agent agent dogstatsd-stats show the per-metric table above, which is the fastest way to confirm a metric actually arrived.

Tests

bin/dev/test          # all three, in order; stops at the first failure
bin/dev/test --unit   # 120 unit/component tests across 11 files
bin/dev/test --e2e    # 6 Playwright specs
bin/dev/test --types  # tsc --noEmit
bin/dev/test --watch  # vitest in watch mode

--e2e installs the Chromium build Playwright expects if it is missing, so a fresh checkout works without a separate setup step.

Coverage is 99.7% of statements and 92.9% of branches, above the 90% floor set in vite.config.ts. Everything was built red-green-refactor.

Notes

  • Node is pinned to 24.20.0 via mise.toml; the repo's system Node was 24.11.1, below the >= 24.13.0 requirement in package.json's engines.
  • The BFF runs .ts directly on Node's native type stripping — no build step.
  • npm 11 gates install scripts; esbuild and the dd-trace native modules are allow-listed in package.json under allowScripts.

About

Simple sample React/Datadog app

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages