How to create a shared context between asynchronous calls in nodejs
Tags: javascript, node, beginners, programming
Published: 2022-05-28 02:02:28

👋 Hello Everyone..!!!

As a javascript developer, even though you don't implement your own [asynchronous](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous) functions very often, you are very likely to need to use them in your everyday project. 

Typically there are two/three ways to deal with asynchronous functions. 

1. Callbacks
2. Promises
3. Async/Await (i.e Promises)

You can read more about these [here](https://levelup.gitconnected.com/asynchronous-javascript-3-ways-5c5434819a14). 

## Problem Statement  

When you have a chain of asynchronous calls (callbacks or promises),  how do you share a common context between all these calls? 

Let's think of the following example.

You are writing a function called `getCustomerOrders()` that returns customer details along with his/her active orders. Inside that function you have to call asynchronous `getCustomer()` and asynchronous `getOrders()` where both of these function needs a `customerId` from in the Request. 

![shared context between asynchronous calls](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/url480baj2bhf98ltbem.png)

Solution is simple right? 😊 

You just extract the `customerId` from the Request and pass it to both `getCustomer()` and `getOrders()` as function parameters. 

```typescript
const getCustomer = async (customerId: string): Promise<Customer> => {
    return fetchCustomerFromApi(customerId);
};
```
Yes, **this is probably the best way** that you share context between asynchronous calls. But do you know an alternative way to share context without passing as parameters? 

## AsyncLocalStorage

`AsyncLocalStorage` class of `async_hooks` module is released as part of [Node.js 14](https://nodejs.org/gl/blog/release/v14.0.0/).

As per the [NodeJS official documentation](https://nodejs.org/api/async_context.html)  
> These classes are used to associate state and propagate it throughout callbacks and promise chains. They allow storing data throughout the lifetime of a web request or any other asynchronous duration. It is similar to thread-local storage in other languages.

In simple terms, this acts as a global variable that scoped to a particular asynchronous execution context. 

## Let's see AsyncLocalStorage in action

Let's see how we can refactor our `getCustomerOrders()` example to use `AsyncLocalStorage` 

1. First, import `AsyncLocalStorage` from `async_hooks` module. 

```typescript
import { AsyncLocalStorage } from "async_hooks";
```
2. Next, you have to create instance from `AsyncLocalStorage` representing the data that you are going to share. In this example we are going to store the `customerId`. 

```typescript
const userAsyncLocalStorage = new AsyncLocalStorage<{ customerId: string }>();
```
3. Now, you have to wrap the `getCustomerOrders()` function using `AsyncLocalStorage.run` function. This is where all the magic happens. As the first parameter to the `run` function, you can pass the data that you want to share.  

```typescript
userAsyncLocalStorage.run(
    {
      // customerId is read from the Request
      customerId: "123456789",
    },
    async () => {
      const customer = await getCustomer();
      const orders = await getOrders();
      // write data to Response 
      console.log({
        customer,
        orders,
      });
    }
  );
```
4. Finally, inside the `getCustomer()` and `getOrders()` you can retrieve `customerId` as below.

```typescript
const getCustomer = async () => {
    const { customerId } = userAsyncLocalStorage.getStore();
    return fetchCustomerFromApi(customerId);
}
```

That is the end of very basic application using `AsyncLocalStorage`.

## Usage

[Global state or variables are generally considered bad](https://wiki.c2.com/?GlobalVariablesAreBad)
 as they make testing and debugging a lot harder. Therefor the pattern of using `AsyncLocalStorage` to share business data across multiple asynchronous calls (like we share `customerId`) is not recommended.

But `AsyncLocalStorage` pattern comes in handy when you develop/use [APM Tools,](https://smartbear.com/learn/performance-monitoring/what-is-application-performance-management/) which collect performance metrics. 

[This post](https://blog.kuzzle.io/nodejs-14-asynclocalstorage-asynchronous-calls) explains how you can use `AsyncLocalStorage` to create a simple logger component. 

Also NodeJS Frameworks like [adonisjs](https://adonisjs.com/) uses `AsyncLocalStorage` extensively during the HTTP requests and set the HTTP context as the state. 

You can read more about that [here](https://docs.adonisjs.com/guides/async-local-storage#how-does-adonisjs-uses-als).

❤️ Appreciate your feedback and thank you very much for reading...!!