Monitoring GraphQL APIs

1. Implementing Query Logging

Example: Envelop logger plugin

plugins: [{
  onExecute({ args }) {
    const start = performance.now();
    return {
      onExecuteDone({ result }) {
        logger.info({
          op: args.operationName,
          variables: args.variableValues,
          ms: performance.now() - start,
          errors: result.errors?.length ?? 0
        });
      }
    };
  }
}]

2. Tracking Query Performance

MetricDetail
p50/p95/p99 latencyPer operationName
Resolver durationPer field timing
ThroughputQPS, mutations/s
DataLoader batchesAverage batch size

3. Implementing Tracing

SpecDetail
Apollo TracingPer-resolver tree of durations
FTV1Federated trace v1
OpenTelemetryVendor-neutral standard

4. Using OpenTelemetry

Example: OTel instrumentation

import { useOpenTelemetry } from "@envelop/opentelemetry";
import { trace } from "@opentelemetry/api";

plugins: [useOpenTelemetry({
  resolvers: true, variables: false, result: false
}, trace.getTracerProvider())]

5. Tracking Error Rates

DimensionDetail
By error codeVALIDATION, FORBIDDEN, INTERNAL
By operationFind regressions per query
By clientapollographql-client-name header
SLO budgetBurn-rate alerts

6. Implementing Health Checks

Example: Liveness + readiness

app.get("/healthz", (_, res) => res.send("ok"));
app.get("/readyz", async (_, res) => {
  try {
    await Promise.all([db.$queryRaw`SELECT 1`, redis.ping()]);
    res.send("ready");
  } catch { res.status(503).send("not ready"); }
});

7. Creating Performance Dashboards

ToolDetail
Apollo StudioOperation insights, traces
Grafana + PrometheusCustom dashboards from metrics
Datadog APMResolver flame graphs
HoneycombHigh-cardinality tracing

8. Alerting on Errors

AlertTrigger
Error rate> 1% INTERNAL errors over 5m
Latencyp95 > 1s for 10m
Subgraph downHealth check failures
Schema driftBreaking change detected

9. Tracking Schema Usage

UseDetail
Field popularityIdentify removable deprecated fields
Per-client usageCoordinate breaking changes
Apollo operation registryCaptures via usage reporting

10. Implementing Request ID

Example: Trace correlation

app.use((req, _, next) => {
  req.id = req.headers["x-request-id"] ?? randomUUID();
  next();
});

context: ({ req }) => ({ requestId: req.id, logger: logger.child({ requestId: req.id }) })