# cls-request-context

> Express, Fastify and Koa middlewares, and Hapi plugin for CLS-based request id generation and properties builder, batteries included

Latest version **2.0.2** (published 2020-02-19) · MIT license · 0 weekly downloads

## Install

```sh
npm install cls-request-context
pnpm add cls-request-context
yarn add cls-request-context
bun add cls-request-context
```

## Health

**Score 15/100 (F)** — status: abandoned.

Positive: no vulnerabilities.

Warnings: low downloads; no types; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 2.0.2 |
| Published | 2020-02-19 |
| First published | 2020-02-07 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | CommonJS |
| Node | >=8.0.0 - <10.0.0 \|\| >=10.4.0 |
| Dependencies | 1 |
| Unpacked size | 18.6 KB |
| Known vulnerabilities | 0 (+1 in 1 direct dependencies) |
| Install scripts | no |
| Author | Andrey Pechkurov |
| Maintainers | marciopd |
| Keywords | express, fastify, koa, hapi, cls, request, request id, logging |

## Links

- npm: https://www.npmjs.com/package/cls-request-context
- Repository: https://github.com/marciopd/cls-rtracer
- Issues: https://github.com/marciopd/cls-rtracer/issues
- npm.io page: https://npm.io/package/cls-request-context

## Dependencies (1)

- [uuid](https://npm.io/package/uuid.md) 3.3.3

## Alternatives

- [cli-color](https://npm.io/package/cli-color.md) — 3.4M weekly downloads
- [log](https://npm.io/package/log.md) — 1.3M weekly downloads
- [logstash-client](https://npm.io/package/logstash-client.md) — 4.5K weekly downloads
- [@nocobase/plugin-logger](https://npm.io/package/@nocobase/plugin-logger.md) — 2.0K weekly downloads
- [child-process-debug](https://npm.io/package/child-process-debug.md) — 695 weekly downloads

## Recent versions

- 2.0.2 (latest) — 2020-02-19
- 2.0.1 — 2020-02-07
- 2.0.0 — 2020-02-07

## README

# cls-request-context

This library is based on [cls-rtracer](https://github.com/puzpuzpuz/cls-rtracer) and makes it easier to add custom CLS properties 
for the request context.

## How to use it - Step 1

Install:

```bash
npm install --save cls-request-context cls-hooked
```

Note: `cls-hooked` has to be installed explicitly, as it's a [peer dependency](https://nodejs.org/es/blog/npm/peer-dependencies/) for this library. See [this issue](https://github.com/puzpuzpuz/cls-rtracer/issues/18) for more details.

Note for TypeScript users: typings are included.

## How to use it - Step 2 (Common instructions)

Use the middleware (or plugin) provided by the library before the first middleware that needs to have access to request ids. Note that some middlewares, e.g. express-session, body-parser, or express-jwt, may cause CLS context (i.e. Async Hooks execution path) to get lost. To avoid such issues, you should use any third party middleware that does not need access to request ids *before* you use this middleware. See issue #20 as an example.

## How to use it - Step 2 (Express users)

Use the middleware provided by the library:

```javascript
const express = require('express')
const rTracer = require('cls-request-context')

const app = express()
// any third party middleware that does not need access to request ids goes here
// ...

app.use(rTracer.expressMiddleware())
// optionally, you can override default middleware config:
// app.use(rTracer.expressMiddleware({
//   useHeader: true,
//   headerName: 'X-Your-Request-Header'
// }))

// all code in middlewares, starting from here, has access to request ids
```

Obtain request id in middlewares on the incoming request:

```javascript
// an example middleware for a generic find entity endpoint
app.get('/api/v1/entity/{id}', (req, res, next) => {
  entityService.find(req.params.id)
    .then((entity) => {
      // you can obtain the request id here
      const requestId = rTracer.id()
      console.log(`requestId: ${requestId}`)
      
      res.json(entity)
    })
    .catch(next)
})
```

You can access the same request id from code that does not have access to the Express' `req` object.

```javascript
// an imaginary entity-service.js
async function find (entityId) {
  // you can obtain the request id here
  const requestId = rTracer.id()
  // ...
}
```

## How to use it - Step 2 (Fastify users)

Use the middleware provided by the library:

```javascript
const fastify = require('fastify')()
const rTracer = require('cls-request-context')

// any third party middleware that does not need access to request ids goes here
// ...

fastify.use(rTracer.fastifyMiddleware())
// optionally, you can override default middleware config:
// fastify.use(rTracer.fastifyMiddleware({
//   useHeader: true,
//   headerName: 'X-Your-Request-Header'
// }))

// all code in middlewares, starting from here, has access to request ids
```

Obtain request id in middlewares on the incoming request:

```javascript
// an example middleware for a generic find entity endpoint
// router config is skipped for the sake of simplicity
app.get('/test', async (request, reply) => {
  const entity = await entityService.find(request.params.id)
  // you can obtain the request id here
  const requestId = rTracer.id()
  console.log(`requestId: ${requestId}`)

  reply.send(entity)
})
```

You can access the same request id from code that does not have access to the Fastify's `request` object.

```javascript
// an imaginary entity-service.js
async function find (entityId) {
  // you can obtain the request id here
  const requestId = rTracer.id()
  // ...
}
```

## How to use it - Step 2 (Koa users)

Use the middleware provided by the library:

```javascript
const Koa = require('koa')
const rTracer = require('cls-request-context')

const app = new Koa()
// any third party middleware that does not need access to request ids goes here
// ...

app.use(rTracer.koaMiddleware())
// optionally, you can override default middleware config:
// app.use(rTracer.koaMiddleware({
//   useHeader: true,
//   headerName: 'X-Your-Request-Header'
// }))

// all code in middlewares, starting from here, has access to request ids
```

Obtain request id in middlewares on the incoming request:

```javascript
// an example middleware for a generic find entity endpoint
// router config is skipped for the sake of simplicity
app.use(async (ctx) => {
  const entity = await entityService.find(req.params.id)
  // you can obtain the request id here
  const requestId = rTracer.id()
  console.log(`requestId: ${requestId}`)

  ctx.body = entity
})
```

You can access the same request id from code that does not have access to the Koa's `ctx` object.

```javascript
// an imaginary entity-service.js
async function find (entityId) {
  // you can obtain the request id here
  const requestId = rTracer.id()
  // ...
}
```

### Koa v1 support

For Koa v1 use the `koaV1Middleware(options)` function.

## How to use it - Step 2 (Hapi users)

Use the plugin provided by the library:

```javascript
const Hapi = require('@hapi/hapi')
const rTracer = require('cls-request-context')

const init = async () => {
  const server = Hapi.server({
    port: 3000,
    host: 'localhost'
  })
  // any third party plugin that does not need access to request ids goes here
  // ...

  await server.register({
    plugin: rtracer.hapiPlugin
  })

  // optionally, you can override default middleware config:
  //  await server.register({
  //    plugin: rtracer.hapiPlugin,
  //    options: {
  //      useHeader: true,
  //      headerName: 'X-Your-Request-Header'
  //    }
  //  })

  // all code in routes, starting from here, has access to request ids
}

init()
```

Obtain request id in route handlers on the incoming request:

```javascript
// an example route for a generic find entity endpoint
server.route({
  method: 'GET',
  path: '/test',
  handler: async (request, h) => {
    const entity = await entityService.find(request.params.id)
    // you can obtain the request id here
    const requestId = rTracer.id()
    console.log(`requestId: ${requestId}`)

    return entity
  }
})
```

You can access the same request id from code that does not have access to the Hapi's `request` object.

```javascript
// an imaginary entity-service.js
async function find (entityId) {
  // you can obtain the request id here
  const requestId = rTracer.id()
  // ...
}
```

## Integration with loggers

The main use case for this library is request id generation and logging automation. You can integrate with any logger library in a single place and get request ids in logs across your application.

Without having request id, as a correlation value, in your logs, you will not be able to determine which log entries belong to code that handles the same request. You could generate request ids manually and store them in the Express' `req` object (or Fastify's `request`, or Koa's `ctx`), but then you will have to explicitly pass the object into all other modules on the route. And that's when `cls-request-context` comes to the rescue!

Here is how you can integrate `cls-rtracer` with [winston](https://github.com/winstonjs/winston), one of most popular logging libraries.

```javascript
const { createLogger, format, transports } = require('winston')
const { combine, timestamp, printf } = format

// a custom format that outputs request id
const rTracerFormat = printf((info) => {
  const rid = rTracer.id()
  return rid
    ? `${info.timestamp} [request-id:${rid}]: ${info.message}`
    : `${info.timestamp}: ${info.message}`
})

const logger = createLogger({
  format: combine(
    timestamp(),
    rTracerFormat
  ),
  transports: [new transports.Console()]
})
```

Complete samples for Express, Fastify and Koa are available in `/samples/` directory.

## Middleware configuration

These are the available config options for the middleware functions. All config entries are optional.

```javascript
{
  // Respect request header flag (default: false).
  // If set to true, the middleware will be using a value from the specified header (if the value is present).
  useHeader: false,
  // Request header name, case insensitive (default: 'X-Request-Id').
  // Used if useHeader is set to true.
  headerName: 'X-Request-Id'
}
```

## Troubleshooting

To avoid weird behavior:

* Make sure you require `cls-request-context` as the first dependency in your app. Some popular packages may use async which breaks CLS.

* Make sure you use any third party middleware (or plugin) that does not need access to request ids *before* you use `cls-request-context`. See [this section](#how-to-use-it---step-2-common-instructions).

Note: there is a small chance that you are using one of rare libraries that do not play nice with Async Hooks API, which is internally used by the `cls-hooked` library. So, if you face the issue when CLS context (and thus, the request id) is lost at some point of async calls chain, please submit GitHub issue with a detailed description.

Note for Node 10 users:

* Node 10.0.x-10.3.x is not supported. That's because V8 version 6.6 introduced a bug that breaks async_hooks during async/await. Node 10.4.x uses V8 v6.7 where the bug is fixed. See: https://github.com/nodejs/node/issues/20274.

## Performance impact

Note that this library has a certain performance impact on your application due to CLS (or more precisely, Async Hooks API) usage. So, you need to decide if the benefit of being able to trace requests in logs without any boilerplate is more valuable for you than the disadvantage of performance impact.

The author of this library did some basic performance testing and got about 10–15% RPS (request per second) degradation when `cls-request-context` is used. See [this post](https://stackoverflow.com/questions/50595130/express-what-load-can-continuation-local-storage-handle/53647537#53647537) for more details.

## License

Licensed under MIT.

---
_Source: https://npm.io/package/cls-request-context · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
