Skip to main content
APM is an opt-in add-on to your existing ApiTraffic integration. Your framework integration (Express, Fastify, Hapi, Koa, Strapi) keeps capturing requests, responses and outbound HTTP; adding @apitraffic/apm adds everything that happens inside the request — queries, cache calls, queues and RPC. All of the OpenTelemetry weight lives in the APM package, so nothing is installed into your application unless you add it.

Install

The OpenTelemetry packages are peer dependencies. If you are already running OpenTelemetry you only need @apitraffic/apm and the packages you have.

Option 1 — Zero configuration

Call register() before your application boots, so the auto-instrumentations can patch pg, redis, mysql2 and friends before your code requires them. The usual way is a preload file:
instrument.js
Or, if you prefer a single entry point, make it the very first import:
server.js
register() is idempotent and fail-open: if OpenTelemetry is missing, or another APM already owns the tracer provider, it returns quietly and your application starts normally.
Registering after your database or cache client has been required means those modules are never patched, and their operations produce no spans. This is the most common reason a request has no waterfall.

Option 2 — You already run OpenTelemetry

Do not double-instrument. Add our span processor to the provider you already have, and we ingest the spans you are already producing:
An ApiTrafficSpanExporter is also exported for pipelines that prefer a SpanExporter behind a BatchSpanProcessor. Both funnel through the same mapping.

Configuration

Options can be passed to register() or set as environment variables. An explicit option wins over the environment variable, which wins over the default.
captureStatementParams and captureRedisKeys can also be turned on from the dashboard for a bucket, without a redeploy and with an automatic expiry, and captureCallSite / captureStackDepth are owned by the dashboard unless you set them here. See capture settings.

Statement text and privacy

Statements and call stacks are stored and shown only for accounts with statement text enabled, everywhere: request pages, shared links, embeds, workflows, the API, the MCP tools and the Performance analytics. For other accounts they are removed before a request is stored (and again on every read, for requests stored earlier); spans keep their operation, fingerprint and call site. See capture settings.

Statement text once per operation

A query’s normalized statement and caller stack are the same every time it runs, and they are the largest part of a span. With sendStatementTextOnce on, a process sends them with the first span of each operation (fingerprint) and leaves them off later spans of that operation for statementTextResendSeconds. Those spans keep their fingerprint, name, type and call site, and list what they left off in textOmitted; the request page fills the text back in from the other spans of the request and from your account’s fingerprint dictionary.
  • Text counts as sent only once the payload was delivered, and it is sent again after the resend interval, so a restart or a failed write on our side heals within that interval.
  • A caller stack is left off only when that exact stack was already sent for the operation; an operation called from several places keeps sending each new stack.
  • Each process keeps its own memory, so a fleet sends each statement once per process per interval.
It is off by default. Turn it on only once the processors that ingest your data maintain the fingerprint dictionary (earlier processors read the statement from every span). Those processors keep the dictionary for every request with spans from an account with statement text enabled, whether or not the account’s plan includes span analytics and whether or not span rows are shipped to the analytics store, so resolution does not depend on the plan. The dictionary holds statement text only for accounts with statement text enabled: for other accounts, repeated spans on the request page show their fingerprint and call site, with a note that query text is not stored for the account. For an account that stores text, a statement the dictionary cannot supply yet (its first write failed, or the lookup timed out) is shown as temporarily unavailable; it heals with the next resend. The memory is per destination: when a process is set up again with a different bucket or API token, it starts empty and sends every statement again. This is a base-client option: pass it with your other ApiTraffic options or set the environment variable; register() of the bridge does not take it.

Outbound HTTP

Outbound calls made with http/https (including axios, got and request) are captured by the base client, with full request and response bodies. Calls made with fetch/undici are captured by the APM bridge as metadata-only spans. You do not need to configure anything for this; leaving instrumentHttp off is what keeps a native call from being recorded twice.

Verify

  1. Start your application and exercise an endpoint that queries a database or calls another API.
  2. Open the request in Traffic and select the Waterfall tab. Each query, cache call and outbound request appears on the timeline with its duration and metadata.
  3. Open Performance to see the same operations aggregated across requests.

Troubleshooting

APM was probably not registered in that process, or was registered too late. register() must run before the database/cache modules are required — use node -r ./instrument.js to be sure. Enable debug: true to see whether the bridge started and whether OpenTelemetry was found.
The driver’s instrumentation was not applied. Check that the driver is one of the supported modules, that it is required after register(), and that it is not listed in disabledInstrumentations.
The per-request span cap (maxSpansPerRequest, default 500) was reached — typically an N+1. The dropped count is retained on the request, and the rollups still include the dropped work.
That is the default. Values are stripped in-process before anything is sent. Turn them on deliberately and temporarily from capture settings.
OpenTelemetry’s http instrumentation is enabled alongside the native hook. Leave instrumentHttp off (the default) unless you have disabled the native hook.