Asynchronous local storage implementation based on Node.js ALS with fallback to cls-hooked for older Node.js versions.
Heavily based on a work done in https://github.com/thorough-developer/fast-als
If you are planning to use it with fastify, it is recommended to use fastify-request-context plugin instead which seamlessly integrates asynchronous-local-storage
with the fastify request lifecycle.
$ npm install --save asynchronous-local-storage
const { als } = require('asynchronous-local-storage')
const fastify = require('fastify')({ logger: true })
const asyncResourceSymbol = Symbol('asyncResource')
fastify.addHook('onRequest', (req, reply, done) => {
als.runWith(() => {
const asyncResource = new AsyncResource('my-als-context')
req[asyncResourceSymbol] = asyncResource
asyncResource.runInAsyncScope(done, req.raw)
}, { user: { id: 'defaultUser' } })
});
fastify.addHook('preValidation', (req, res, done) => {
const asyncResource = req[asyncResourceSymbol]
asyncResource.runInAsyncScope(done, req.raw)
})
fastify.addHook('preHandler', (req, reply, done) => {
// overrides default user value
als.set('user', { id: 'customUser' });
done();
});
// Declare a route
fastify.get('/', async (request, reply) => {
return {
user: als.get('user')
}
});
// Run the server!
const start = async () => {
try {
await fastify.listen(3000);
fastify.log.info(`server listening on ${fastify.server.address().port}`);
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
}
start();
const { als } = require('asynchronous-local-storage')
const express = require('express')
const app = express()
const port = 3000
app.use((req, res, next) => {
als.runWith(() => {
next();
}, { user: { id: 'defaultUser' } }); // sets default values
});
app.use((req, res, next) => {
// overrides default user value
als.set('user', { id: 'customUser' });
next();
});
app.get('/', (req, res) => res.send({ user: als.get('user') }))
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
Start an asynchronous local storage context. Once this method is called, a new context is created, for which
get
and set
calls will operate on a set of entities, unique to this context.
callback
- function that will be executed first within the newly created context[defaults]
- an optional record, containing default values for the context
const als = require('asynchronous-local-storage');
function firstCallInScope() {
// override user
als.set('user', { id: 'overwrittenUser'});
}
function secondCallInScope() {
// will print the user set in firstCallInScope
console.log(als.get('user'));
}
als.runWith(() => {
firstCallInScope();
secondCallInScope();
}, { user: { id: 'someUser' } });
Sets a variable for a given key within running context (started by runWith
).
If this is called outside of a running context, it will not store the value.
key
a string key to store the variable byvalue
any value to store under the key for the later lookup.
const als = require('asynchronous-local-storage');
function callInScope() {
// override user
als.set('user', { id: 'overwrittenUser'});
}
als.runWith({ user: { id: 'someUser' } }, () => {
callInScope();
});
const als = require('asynchronous-local-storage');
function callOutOfScope() {
// this never gets set
als.set('user', { id: 'overwrittenUser'});
}
// calling this won't store the variable under the key
callOutOfScope();
Gets a variable previously set within a running context (started by runWith
).
If this is called outside of a running context, it will not retrieve the value.
key
a string key to retrieve the stored value for
const als = require('asynchronous-local-storage');
function callInScope() {
// prints default user
console.log(als.get('user'));
}
als.runWith(() => {
callInScope();
}, { user: { id: 'someUser' } });
const als = require('asynchronous-local-storage');
function callInScope() {
// prints default user
console.log(als.get('user'));
}
als.runWith(() => {
callInScope();
}, { user: { id: 'someUser' } });
// calling this will return undefined
callInScope();