Can shared NPM packages access Async Local Storage context?

Hey folks,

I’m stuck with a tricky problem. I made a private NPM package for my services to share stuff like logging and HTTP clients. Now I want to add Async Local Storage context handling to it too. But it’s not working as expected.

Here’s what I’ve got:

// In my shared package
import { AsyncLocalStorage } from 'node:async_hooks';

const userStore = new AsyncLocalStorage();

function runUserContext(userData, callback) {
  return userStore.run(userData, callback);
}

function getCurrentUser() {
  return userStore.getStore();
}

// In my logging function
function logStuff(message) {
  const user = getCurrentUser(); // This doesn't work
  console.log(message, { userId: user?.id });
}

// In my service
await runUserContext({ id: 'abc123' }, async () => {
  const user = getCurrentUser(); // This works fine
});

The context works in my service code, but I can’t access it in the shared library. Am I missing something or is this approach even possible?

Interesting problem you’ve encountered, SparklingGem. The issue lies in how Node.js handles modules and AsyncLocalStorage. Each module gets its own separate instance of ALS, which is why you’re not seeing the expected behavior across different modules.

To resolve this, consider creating a separate module specifically for your ALS setup. Export both the AsyncLocalStorage instance and the associated functions (runUserContext, getCurrentUser) from this module. Then, import this module in both your shared package and your service code.

This approach ensures that all parts of your application are using the same ALS instance, allowing for consistent context sharing across your codebase. It’s a common pattern for managing global state in Node.js applications.

I’ve dealt with this exact problem in a microservices architecture. The key is to understand that Node.js treats each npm package as a separate module with its own scope. Here’s what worked for me:

Create a dedicated ‘context’ package that exports the ALS instance and related functions. Then import this in both your shared utilities and your services. This way, everyone’s on the same page.

Also, watch out for how you’re structuring your async operations. I found that some Promise chains were breaking the context propagation. Using async/await consistently helped maintain the context better.

One last tip - if you’re using Express, look into the ‘express-async-local-storage’ middleware. It saved me tons of headaches by automatically setting up the context for each request. Just remember to keep your dependencies in check to avoid version conflicts across services.

hey there sparklinggem! i ran into this issue too. the problem is that ALS context is specific to each module. try exporting the ALS instance from your shared package and importing it directly in your service. that way both use the same instance. hope this helps!