Skip to content

Commit e4585e9

Browse files
committed
docs(recipes): Address remarks from PR
1 parent 7ddca39 commit e4585e9

File tree

1 file changed

+33
-30
lines changed

1 file changed

+33
-30
lines changed

content/recipes/async-local-storage.md

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
The main idea of Async Local Storage is that we can _wrap_ some function call with the `AsyncLocalStorage#run` call. All code that is invoked within the wrapped call gets access to the same `store`, which will be unique to each call chain.
66

7-
In the context of NestJS, that means if we can find a place within the request's lifecycle where we can wrap the rest of the request's code, we will be able to access and modify state visible only to that request, effectively eliminating the need for REQUEST scoped providers and their limitations.
7+
In the context of NestJS, that means if we can find a place within the request's lifecycle where we can wrap the rest of the request's code, we will be able to access and modify state visible only to that request, which may serve as an alternative to REQUEST-scoped providers and some of their limitations.
8+
9+
Alternatively, we can use ALS to propagate context for only a part of the system (for example the _transaction_ object) without passing it around explicitly across services, which can increase isolation and encapsulation.
810

911
#### Custom implementation
1012

@@ -17,9 +19,6 @@ NestJS itself does not provide any built-in abstraction for `AsyncLocalStorage`,
1719
```ts
1820
/** als.module.ts */
1921

20-
import { AsyncLocalStorage } from 'async_hooks';
21-
import { Module } from '@nestjs/core';
22-
2322
@Module({
2423
providers: [
2524
{
@@ -31,37 +30,41 @@ import { Module } from '@nestjs/core';
3130
})
3231
export class AlsModule {}
3332
```
33+
> info **Hint** `AsyncLocalStorage` is imported from `async_hooks`.
3434
3535
2. We're only concerned with HTTP, so let's use a middleware to wrap the `next` function with `AsyncLocalStorage#run`. Since a middleware is the first thing that the request hits, this will make the `store` available in all enhancers and the rest of the system.
3636

3737
```ts
38-
/** main.ts */
39-
40-
import { NestFactory } from '@nestjs/core';
41-
import { AppModule } from './app.module';
42-
import { AsyncLocalStorage } from './als.module.ts';
43-
44-
async function bootstrap() {
45-
const app = await NestFactory.create(AppModule);
46-
47-
// Retrieve the instance from the container
48-
// (given we've imported the AlsModule in our AppModule).
49-
const als = app.get(AsyncLocalStorage);
50-
51-
// Here we can bind the middleware to all routes,
52-
app.use((req, res, next) => {
53-
// populate the store with some default values
54-
// based on the request,
55-
const store = {
56-
userId: req.headers['x-user-id'],
57-
};
58-
// and pass the "next" function as callback
59-
// to the "als.run" method together with the store.
60-
als.run(store, () => next());
61-
});
62-
}
38+
/** app.module.ts */
6339

64-
bootstrap();
40+
@Module({
41+
imports: [AlsModule]
42+
providers: [CatService],
43+
controllers: [CatController],
44+
})
45+
export class AppModule implements NestModule {
46+
constructor(
47+
// inject the AsyncLocalStorage in the module constructor,
48+
private readonly als: AsyncLocalStorage
49+
) {}
50+
51+
configure(consumer: MiddlewareConsumer) {
52+
// bind the middleware,
53+
consumer
54+
.apply((req, res, next) => {
55+
// populate the store with some default values
56+
// based on the request,
57+
const store = {
58+
userId: req.headers['x-user-id'],
59+
};
60+
// and and pass the "next" function as callback
61+
// to the "als.run" method together with the store.
62+
als.run(store, () => next());
63+
})
64+
// and register it for all routes (in case of Fastify use '(.*)')
65+
.forRoutes('*');
66+
}
67+
}
6568
```
6669

6770
3. Now, anywhere within the lifecycle of a request, we can access the local store instance.

0 commit comments

Comments
 (0)