thrilled-be-core
Version:
Core Express backend package with middleware, logging, security, and base application setup
150 lines (149 loc) • 4.88 kB
JavaScript
;
/**
* Correlation ID Middleware for Request Tracking
*
* This middleware ensures that every request has a unique correlation ID
* that can be used for tracking requests across the application, logs,
* and external services. This is essential for distributed tracing and
* debugging in microservices architectures.
*
* @package be-core
* @since 1.0.0
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.CorrelationIdMiddleware = void 0;
exports.createCorrelationIdMiddleware = createCorrelationIdMiddleware;
exports.correlationId = correlationId;
const crypto_1 = require("crypto");
const Logger_1 = require("../logging/Logger");
/**
* Correlation ID Middleware for tracking requests across distributed systems
*/
class CorrelationIdMiddleware {
logger;
options;
constructor(options = {}) {
this.logger = options.logger || new Logger_1.Logger({ level: 'info', dir: './logs' });
this.options = {
logger: this.logger,
headerName: 'X-Correlation-ID',
propertyName: 'correlationId',
generateId: () => (0, crypto_1.randomUUID)(),
enforceHeader: false,
logRequests: process.env.NODE_ENV !== 'test',
...options,
};
}
/**
* Main correlation ID middleware
*/
handle() {
return (req, res, next) => {
const correlationId = this.extractOrGenerateCorrelationId(req);
// Attach correlation ID to request using custom property name
req[this.options.propertyName] = correlationId;
// Also set the standard correlationId for compatibility
req.correlationId = correlationId;
req.requestId = correlationId; // Alias for compatibility
// Set response header
res.setHeader(this.options.headerName, correlationId);
// Log request if enabled
if (this.options.logRequests) {
this.logRequest(req);
}
next();
};
}
/**
* Extract correlation ID from request or generate new one
*/
extractOrGenerateCorrelationId(req) {
// Try to get correlation ID from various header names
const headerSources = [
this.options.headerName,
'X-Request-ID',
'X-Trace-ID',
'Request-ID',
'Trace-ID',
];
for (const headerName of headerSources) {
const value = req.get(headerName);
if (value && this.isValidCorrelationId(value)) {
return value;
}
}
// Generate new correlation ID
return this.options.generateId();
}
/**
* Validate correlation ID format
*/
isValidCorrelationId(id) {
// Basic validation - should be non-empty string with reasonable length
return typeof id === 'string' &&
id.length > 0 &&
id.length <= 128 &&
!/[<>"'&]/.test(id); // Basic XSS protection
}
/**
* Log incoming request with correlation ID
*/
logRequest(req) {
try {
this.logger.info('Incoming request', {
correlationId: req.correlationId,
method: req.method,
url: req.originalUrl,
userAgent: req.get('User-Agent'),
ip: req.ip,
timestamp: new Date().toISOString(),
});
}
catch (error) {
// Silently fail if logging fails - don't break the request flow
// The correlation ID is still attached to the request
}
}
/**
* Get correlation ID from request
*/
static getCorrelationId(req) {
return req.correlationId;
}
/**
* Get correlation ID from request with fallback
*/
static getCorrelationIdSafe(req) {
return req.correlationId || 'unknown';
}
/**
* Health check for correlation ID middleware
*/
healthCheck() {
return {
name: 'correlation-id-middleware',
status: 'healthy',
timestamp: new Date().toISOString(),
details: {
headerName: this.options.headerName,
propertyName: this.options.propertyName,
enforceHeader: this.options.enforceHeader,
logRequests: this.options.logRequests,
},
};
}
}
exports.CorrelationIdMiddleware = CorrelationIdMiddleware;
/**
* Factory function to create correlation ID middleware
*/
function createCorrelationIdMiddleware(options) {
return new CorrelationIdMiddleware(options);
}
/**
* Express middleware function for correlation ID
*/
function correlationId(options) {
const middleware = new CorrelationIdMiddleware(options);
return middleware.handle();
}