Continuation Local Storage for easy context passing in Node.js

Roman Coedo
Trabe
Published in
2 min readOct 14, 2019
Photo by Suad Kamardeen on Unsplash

Recently I’ve been working on a Node.js project where we needed to keep track of which requests were generating calls to some other pieces of code. Passing a request identifier around was not viable since it would require to change too many APIs.

We solved the problem using cls-hooked, a small package that uses the async_hooks experimental Node.js API to implement Continuation Local Storage.

What is Continuation Local Storage?

Some languages have Thread Local Storage. This is a way to attach global data to the thread itself. Due to the asynchronous and single-threaded nature of Node.js this is not useful. Instead, we use Continuation Local Storage (CLS).

CLS is a mechanism that allows the attachment of data to the current asynchronous execution context. It uses async_hooks to keep track of asynchronous context changes and to load and unload the data associated with it.

Building an Express middleware

First we need to create a namespace. Namespaces are a way to scope global data so we don’t end up getting values from somebody else. We create a namespace with the createNamespace function.

Our express middleware is going to be quite simple. We’re going to generate an uuid and store it in the namespace.

As you can see, the set method is called within a run. The requestId value is going to be available from any code called inside that run block, and that’s why we are calling next() inside the run as well. Calling next() inside the run will make the requestId available for any other middleware and controller in our express application.

We can now build our complete express application and use the requestId anywhere:

A word on performance

Even though the solution presented above is clean and requires little coding, it has a major drawback: the usage of the async_hooks API.

The async_hooks API is still experimental so it could possibly change in the future, but what is worse is that enabling it has a big impact on performance. This is currently being discussed in this github issue.

Benedikt Meurer wrote this great benchmark comparing performances with and without async_hooks enabled.

If you are writing a critical piece of code you probably want to stay away from async_hooks. You’d also want to stay away from async_hooks if you’re writing a library, since you could impact performance for your users just for your convenience. If the problem you’re dealing with does not match neither of these scenarios, CLS may be a great way of making things simpler.

Enjoy!

--

--