/**
* @file express middleware to handle request id's and traces
* @author Florian Schaper <f.schaper@reply.de>
* @copyright Florian Schaper 2018, MIT License
*/
'use strict';
// using random version of the uuid formats
const { v4: uuidv4 } = require('uuid');
/**
* returns a unique id for every call
* @callback UUIDGenerator
*/
/**
* @typedef RequestIdConfiguration
* @type {Object}
* @property {string} serviceName - name of the service that will be appended to the trace id string
* @property {string} [traceHeader] - the request header to take the trace id from. defaults to `X-Amzn-Trace-Id`.
* @property {string} [requestIdHeader] - the request header to take the request id from. defaults to `x-amzn-RequestId`.
* @property {boolean} [setResponseHeader] - this will add the current `options.traceHeader` key and value to the headers of the response object. defaults to true.
* @property {UUIDGenerator} [uuidGenerator] - function that will return an unique id. by default v4 uuids will be used.
*/
/**
* @callback RequestMiddleware
* @param {Object} req the express request object (see http://expressjs.com/en/4x/api.html#req)
* @param {Object} res the express response object (see http://expressjs.com/en/4x/api.html#res)
* @param {Object} next the next middleware object to be executed from the express middleware list
*/
/**
* searches for a trace id in the request headers or otherwise creates one
*
* we use a trace id for requests in order to associate multiple e.g. log-entries or cross service calls
* to an specific incoming request
*
* @param {RequestIdConfiguration} options - configuration options for this middleware
* @returns {RequestMiddleware}
* @throws TypeError in case that the middleware has been passed incorrectly to express
* @throws TypeError if not passed an options object
* @throws TypeError if not given a serviceName with the options
*/
function requestId(options) {
// ensure that the middleware has not been passed as a function reference
if (arguments.length === 3) {
throw new TypeError('the requestId middleware has to be passed via app.use(requestId()); and not as a reference e.g. app.use(requestId);');
}
if (typeof options !== 'object') {
throw new TypeError('the requestId middleware expects to be passed an options object');
}
if (typeof options.serviceName !== 'string') {
throw new TypeError('you need to pass a serviceName to the requestId middleware');
}
// overwrite default values (if any)
const {
serviceName,
traceHeader,
requestIdHeader,
setResponseHeader,
uuidGenerator
} = {
traceHeader: 'X-Amzn-Trace-Id',
requestIdHeader: 'x-amzn-RequestId',
setResponseHeader: true,
uuidGenerator: uuidv4,
...options
};
return (req, res, next) => {
// explicitly set the request id
req.requestId = req.get(requestIdHeader) || uuidGenerator();
req.trace = `${serviceName}=${req.requestId};${req.get(traceHeader) || ''}`;
req.traceHeader = traceHeader;
if (setResponseHeader) {
res.set(traceHeader, req.trace);
}
// execute the next middleware module
next();
};
}
module.exports = { requestId };