Context Dependent Imports Using CLS and Getters

Roman Coedo
Trabe
Published in
2 min readJun 15, 2020
100% unrelated photo by Jeremy Bishop on Unsplash

As I was reading this article about exporting getters in CommonJS from my colleague David Barral the other day, it occurred to me that we could use this approach to export dynamically from a CLS context. Since I recently wrote a post about AsyncLocalStorage, I thought that I could probably dig a bit deeper on this subject.

Refactoring our CLS example

In my previous AsyncLocalStorage post I used this example:

We can make it way more readable by taking out all the CLS stuff and moving it to a separate file. This is the content of context.js:

We added a couple of lines here. We defined a default store and we assigned it to the AsyncLocalStorage with the enterWith method. This store will be returned when we’re out of the request scope.

This is our main.js using the new module:

Using the getter magic

The cool thing about getter exports being dynamic is we can encapsulate the logic of taking the requestId out of our context in the context.js module.

We add the requestId getter to the module.exports, and the result looks like this:

Since now we can access requestId just like any other attribute from that module, we get this nice API:

If we access ctx.requestId out of scope we get a not-in-a-request id, otherwise we get the proper request identifier. We can expand the context.js API as much as we want by adding more getters to the module.exports.

Careful, this is magic

Exporting getters is a good way to make clean APIs but if we are not aware that we are using them bad things can happen. Let’s check this other scenario:

If we make an HTTP request to the service now we get this:

[not-in-a-request] request received

What’s happening here?

We are destructuring the module outside of scope. Since requestId is a getter and we are using the destructuring assignment at the top level, we are assigning its value outside of the request scope. This causes not-in-a-request to be assigned to the requestId variable.

Should you do this?

This is the same question David Barral asked at the end of his post. To be honest, I wasn’t a big fan of exporting getters the first time I read about it. For this specific use case though, I think it makes things more readable and clear.

You can find the example code in this repository.

Enjoy!

--

--