node-cls
Version:
Continuation Local Storage based on async_hooks
126 lines (107 loc) • 4.22 kB
Markdown
__Continuation Local Storage__
-------------------------------------
[![npm package][npm-image]][npm-url]
[![Build Status][travis-image]][travis-url]
[![Coverage Status][coveralls-image]][coveralls-url]
[![Dependencies Status][david-image]][david-url]
The purpose with this module is to share contexts across async (and sync) calls. Contexts are accessed by keys and can be nested. It is an alternative to the deprecated [domain](https://nodejs.org/docs/latest-v8.x/api/domain.html). It is based on [async_hooks](https://nodejs.org/docs/latest-v8.x/api/async_hooks.html) that were introduced in node 8. Beware that that the async_hooks are still experimental in nodejs.
__To avoid weird behavior with express__
1. Make sure you require `node-cls` in the first row of your app. Some popular packages use async which breaks CLS.
1. If you are using `body-parser` and context is getting lost, register it in express before you register `node-cls`'s middleware.
__Request handler__
A typical scenario is when you need to share context in a request handler.
```js
let http = require('http');
let cls = require('node-cls');
let server = http.createServer(function (request, response) {
let context = cls.create('request-context');
context.id = 1;
context.request = request;
context.response = response;
context.run(doWork);
})
server.listen(8080)
function doWork() {
let context = cls.get('request-context');
context.response.end(`End: ${context.id}`) //End: 1
}
```
__Async calls__
Context is retained in async calls.
```js
let cls = require('node-cls');
let context = cls.create('myContext');
context.run(() => {
context.name = 'George';
setTimeout(onTimeout, 300);
});
function onTimeout() {
let context = cls.get('myContext');
console.log(context.name); //George
}
```
__Nesting__
Contexts can be nested, even on the same key.
```js
let cls = require('node-cls');
let context = cls.create('myContext');
context.run(async () => {
context.name = 'George';
let context2 = cls.create('myContext');
await context2.run(onNested);
console.log(context.name) //George
});
async function onNested() {
await Promise.resolve();
let context = cls.get('myContext');
console.log(context.name); //undefined
context.name = 'John Nested';
setTimeout(onTimeout, 300);
}
function onTimeout() {
let context = cls.get('myContext');
console.log(context.name); //John Nested
}
```
__Symbol as key__
If you are a library author, use a Symbol as key to avoid conflicts with other libraries.
```js
let cls = require('node-cls');
let key = Symbol();
let context = cls.create(key);
context.run(() => {
context.name = 'George';
setTimeout(onTimeout, 300);
});
function onTimeout() {
let context = cls.get(key);
console.log(context.name); //George
}
```
__Await instead of run__
In node 12 and above you can start a context directly instead of wrapping it in the run function. The start function returns a promise. You can leave the current context by calling exit.
```js
let cls = require('node-cls');
async function main() {
let context = cls.create('myContext');
context.name = 'George';
await context.start();
let context2 = cls.create('myContext');
context2.name = 'John Nested';
await context2.start();
console.log(cls.get('myContext').name); //John Nested
cls.exit('myContext');
console.log(cls.get('myContext').name); //George
cls.exit('myContext');
console.log(cls.get('myContext')); //undefined
}
main();
```
[ ]:https://img.shields.io/npm/v/node-cls.svg
[ ]:http://npmjs.org/package/node-cls
[ ]:https://travis-ci.org/alfateam/node-cls.svg?branch=master
[ ]:https://travis-ci.org/alfateam/node-cls
[ ]:https://david-dm.org/alfateam/node-cls/status.svg
[ ]:https://david-dm.org/alfateam/node-cls
[ ]:https://coveralls.io/repos/github/alfateam/node-cls/badge.svg?branch=master
[ ]:https://coveralls.io/github/alfateam/node-cls?branch=master