Source: index.js

/**
 * @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 };