exp-correlator
Version:
Correlation-id handler and Express middleware
162 lines (123 loc) • 5.26 kB
Markdown
# exp-correlator
[](https://github.com/BonnierNews/exp-correlator/actions/workflows/run-tests.yml)
Keep track of correlation id through a series of async operations. Either using a handler or an Express middleware. Uses
[async_hooks](https://nodejs.org/docs/latest-v16.x/api/async_hooks.html).
## Table of contents
- [exp-correlator](#exp-correlator)
- [Table of contents](#table-of-contents)
- [Installation](#installation)
- [Usage](#usage)
- [Async handler](#async-handler)
- [Express middleware](#express-middleware)
- [Logging with pino](#logging-with-pino)
- [Making requests with exp-fetch](#making-requests-with-exp-fetch)
- [Known issues](#known-issues)
- [Changelog](#changelog)
- [License](#license)
## Installation
```bash
npm install exp-correlator
```
## Usage
If an execution is started using either `attachCorrelationIdHandler` or occurs after the `middleware` is attached (when
the execution is part of the call chain of a Express request) any call to `getId` during that execution will return the
same correlation id. Example of use-cases may be to pass a correlation-id onto subsequent requests to other systems or
to be able to group log messages during the same request without passing on a correlation id between each function call.
### Async handler
The async handler will set the correlation id to the supplied or generate a new uuid v4 if not supplied.
```js
const { attachCorrelationIdHandler, getId } = require("exp-correlator");
const logThis = async function (msg) {
const correlationId = getId();
console.log({correlationId, msg});
}
const f = async function () {
...
logThis("epic message");
logThis("epic message2");
...
};
await attachCorrelationIdHandler(f);
```
In the example above all the log messages produced by logThis can be grouped together by the correlation id without having
to pass the correlation id as an argument every time.
### Express middleware
The Express middleware will set the correlation id from the `correlation-id` or `x-correlation-id` header if available. Otherwise a
new uuid v4 will be generated. The middleware also assigns the correlation-id to the response, matching the header of incoming correlation-id if
it's available.
```js
const { middleware, getId } = require("exp-correlator");
const express = require("express");
const app = express();
const logThis = async function (msg) {
const correlationId = getId();
console.log({correlationId, msg});
}
const callToExternalSystem () {
const correlationId = getId();
await ... // call to an external system setting correlationId as a header
}
app.use(middleware);
app.get("/", (req, res) => {
const correlationId = getId();
await callToExternalSystem();
logThis("epic message");
res.json({ correlationId });
...
});
```
In the example above the middleware set the correlation id based on the incoming header, it will then
be passed on when doing calls to `callToExternalSystem` and `logThis` without being explicitly passed.
### Logging with pino
To add correlationId when logging using [pino](https://www.npmjs.com/package/pino) do the following:
```js
const { getId } = require("exp-correlator");
const pino = require("pino");
const logger = pino({mixin: () => {return { correlationId: getId() };}});
```
In the example above the correct correlation id will be added each time a log function is called when
the express middleware or the async handler is used.
### Making requests with exp-fetch
To add a correlation id when fetching using [exp-fetch](https://www.npmjs.com/package/exp-fetch) do the
following:
```js
const { getId } = require("exp-correlator");
const fetchBuilder = require("exp-fetch);
const fetch = fetchBuilder({ getCorrelationId: getId }).fetch;
await fetch("http://foo.bar");
```
In the example above the correlation id will be added to the outgoing requests headers as `correlation-id`.
## Known issues
Using `bodyParser` after using correlation middleware will cause the async local storage to be undefined.
This applies to any middleware that is derived from `bodyParser` as well (i.e. `urlencoded`).
```js
const { middleware, getId } = require("exp-correlator");
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
const logThis = async function (msg) {
const correlationId = getId(); // correlationId will be null here
console.log({correlationId, msg});
}
app.use(middleware);
// this won't work, bodyParser is used after correlation middleware for this route
app.post("/", bodyParser.json(), (req, res) => {
const correlationId = getId();
await callToExternalSystem();
logThis("epic message");
res.json({ correlationId });
...
});
// workaround add the correlation middleware after the bodyParser
app.post("/", bodyParser.json(), middleware, (req, res) => {
const correlationId = getId();
await callToExternalSystem();
logThis("epic message");
res.json({ correlationId });
...
});
```
## Changelog
Can be found [here](CHANGELOG.md).
## License
Released under the [MIT license](https://tldrlegal.com/license/mit-license).