AWS X-Ray SDK Express Middleware
The `aws-xray-sdk-express` package provides middleware functions for Express.js applications to integrate with AWS X-Ray, enabling distributed tracing for incoming requests and outgoing responses. As part of the broader `aws-xray-sdk-node` monorepo, its current stable version is 3.12.0, with regular updates following the main SDK. It supports both automatic and manual tracing modes. The automatic mode, which is the default, utilizes the `cls-hooked` package for automatic context propagation across asynchronous operations, simplifying segment and subsegment management. Key differentiators include its official AWS support, seamless integration with Express.js routing, and the flexibility to choose between automatic and manual tracing paradigms to suit different application complexities and debugging needs. It requires Express 4.14.0 or greater and `aws-xray-sdk-core` as a peer dependency.
Common errors
-
ReferenceError: AWSXRay is not defined
cause The `aws-xray-sdk-core` module was not imported or required correctly, or `AWSXRay` was used before its declaration.fixEnsure `import * as AWSXRay from 'aws-xray-sdk-core';` (ESM) or `const AWSXRay = require('aws-xray-sdk-core');` (CommonJS) is present at the top of your file. -
TypeError: segment.addAnnotation is not a function
cause The `segment` variable is `null` or `undefined`, indicating no X-Ray segment is currently available in the context or the request was not sampled.fixCheck if `AWSXRay.getSegment()` returns a valid segment before attempting to call methods on it, e.g., `const segment = AWSXRay.getSegment(); if (segment) { segment.addAnnotation(...); }`. Verify that `openSegment` middleware is correctly placed and X-Ray sampling rules allow tracing for the incoming request. -
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
cause The `xrayExpress.closeSegment()` middleware is placed after a response has already been sent to the client, or an error handler sends a response and then `closeSegment` attempts to modify headers.fixEnsure `xrayExpress.closeSegment()` is the first middleware after all your routes, typically before any generic error-handling middleware that might send responses. -
No X-Ray segment found in context.
cause This warning (often from logs) indicates that an operation attempting to access the current X-Ray segment (e.g., `AWSXRay.getSegment()`) was executed outside of an active trace segment context, or the request was not sampled.fixVerify that `xrayExpress.openSegment` is correctly applied to all desired routes and that the X-Ray daemon is running and reachable if you expect traces to be sent. Ensure `AWS_XRAY_CONTEXT_MISSING` is set to 'LOG_ERROR' or 'IGNORE' if you want to control how missing contexts are handled (default is LOG_ERROR).
Warnings
- gotcha The order of `openSegment` and `closeSegment` middleware is critical in automatic mode. `openSegment` MUST be the last middleware before your routes, and `closeSegment` MUST be the first middleware after your routes (or before error handlers) to ensure correct context propagation and segment closure.
- gotcha Automatic tracing mode relies on `cls-hooked` for asynchronous context propagation. While generally robust, complex asynchronous patterns or mixing `cls-hooked` with other context management libraries can lead to context loss and missing trace data.
- gotcha Distinguish between automatic mode segment access (`AWSXRay.getSegment()`) and manual mode segment access (`req.segment`). Using the wrong method for your configured mode will result in `undefined` segments.
- breaking The `aws-xray-sdk-node` monorepo, which includes `aws-xray-sdk-express`, deprecated version `3.7.0`. While this specific package is at 3.12.0, be aware of deprecations and breaking changes in the core SDK when upgrading entire `aws-xray-sdk-node` packages.
Install
-
npm install aws-xray-sdk-express -
yarn add aws-xray-sdk-express -
pnpm add aws-xray-sdk-express
Imports
- xrayExpress
import { openSegment } from 'aws-xray-sdk-express';import * as xrayExpress from 'aws-xray-sdk-express';
- AWSXRay
import { getSegment } from 'aws-xray-sdk-core';import * as AWSXRay from 'aws-xray-sdk-core';
- Segment
import { Segment } from 'aws-xray-sdk-core';import type { Segment } from 'aws-xray-sdk-core';
Quickstart
import express, { Request, Response, NextFunction } from 'express';
import * as AWSXRay from 'aws-xray-sdk-core';
import * as xrayExpress from 'aws-xray-sdk-express';
// Configure X-Ray SDK (defaults to environment variables like AWS_REGION, AWS_XRAY_DAEMON_ADDRESS)
// For local testing without a daemon, you might set the logger to console and disable daemon sending.
// AWSXRay.setLogger(console);
// AWSXRay.setDaemonAddress('127.0.0.1:2000'); // Default
const app = express();
const PORT = process.env.PORT || 3000;
// 1. Initialize X-Ray: This middleware *must* be added before defining any routes to be traced.
// In automatic mode, it is crucial that this is the LAST middleware added before your routes.
// 'ExpressMicroservice' is the name that will appear for your service in X-Ray traces.
app.use(xrayExpress.openSegment('ExpressMicroservice'));
// Define your application routes
app.get('/', (req: Request, res: Response) => {
// In automatic mode, getSegment() retrieves the current segment from the context.
const segment = AWSXRay.getSegment();
if (segment) {
segment.addAnnotation('route', 'home');
segment.addMetadata('user', { id: 'test-user-123', session: 'abc' });
console.log(`Tracing home route within segment: ${segment.id}`);
} else {
console.warn('No X-Ray segment found for home route.');
}
res.send('Hello from X-Ray Traced Express App!');
});
app.get('/data', async (req: Request, res: Response) => {
const segment = AWSXRay.getSegment();
if (segment) {
// Create a subsegment for an internal asynchronous operation
const subsegment = segment.addNewSubsegment('fetchDataOperation');
subsegment.addAnnotation('data_type', 'user_profile');
await new Promise(resolve => setTimeout(resolve, 100)); // Simulate async work
subsegment.close(); // Important to close subsegments
console.log(`Tracing data route within segment: ${segment.id}`);
} else {
console.warn('No X-Ray segment found for data route.');
}
res.json({ message: 'Data fetched successfully' });
});
// 2. Close X-Ray Segment: This middleware *must* be added after all routes to be traced.
// In automatic mode, it must be the FIRST middleware added AFTER your routes (or before error handlers).
app.use(xrayExpress.closeSegment());
// Optional: Error handling middleware for tracing errors
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
if (AWSXRay.isCurrentSegmentAvailable()) {
AWSXRay.getSegment()?.addError(err);
}
console.error('An error occurred:', err.message);
res.status(500).send('Something broke!');
});
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
console.log('Ensure X-Ray daemon is running locally or AWS credentials/environment variables are configured for tracing.');
});