fix: resolve New Relic ghost traffic and blind APM transaction naming

Three root causes addressed:

1. nginx routing gap — bare GET /api (no trailing slash) fell through
   `location /api/` to the Vite dev proxy, which forwarded it to the
   backend as an unmatched path. Added `location = /api` exact-match
   block before the prefix block to catch it and proxy directly to
   the backend health handler.

2. AppController root handler — added @Get() (maps to GET /api with
   global prefix) so bare /api requests return a clean 200 instead of
   a 404 that registers as a phantom NR transaction.

3. New Relic transaction naming — NestJS's setGlobalPrefix('api')
   causes NR's Express instrumentation to bucket ALL requests into the
   generic "Expressjs/GET/api$" segment, making per-endpoint APM data
   completely useless. The new NewRelicTransactionInterceptor calls
   newrelic.setTransactionName() with "METHOD /route/pattern" for
   every request (after routing, so req.route is populated with the
   matched template). Gracefully no-ops in dev where NR is not loaded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-22 09:27:31 -04:00
parent 72161f81f5
commit ce3dc79e47
4 changed files with 84 additions and 0 deletions

View File

@@ -10,6 +10,22 @@ server {
listen 80;
server_name localhost;
# Exact match for bare /api (no trailing slash).
# nginx's `location /api/` below requires a trailing slash, so a request for
# GET /api would fall through to the Vite proxy, which then forwards it to
# the backend — arriving as an unmatched path that New Relic registers as
# the phantom "Expressjs/GET/api$" transaction bucket.
# This exact-match block catches it first and proxies it directly to the
# backend, where AppController's @Get() handler returns a clean 200.
location = /api {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# API requests -> NestJS backend
location /api/ {
proxy_pass http://backend;