Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/nestjs-steroids/async-context
:link: NestJS Async Context based on async_hooks
https://github.com/nestjs-steroids/async-context
async-context asynchooks asyncstorage nestjs nodejs request-context requestid
Last synced: 8 days ago
JSON representation
:link: NestJS Async Context based on async_hooks
- Host: GitHub
- URL: https://github.com/nestjs-steroids/async-context
- Owner: nestjs-steroids
- License: mit
- Created: 2020-07-03T18:44:49.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2023-10-17T11:14:56.000Z (about 1 year ago)
- Last Synced: 2024-10-24T19:09:01.616Z (15 days ago)
- Topics: async-context, asynchooks, asyncstorage, nestjs, nodejs, request-context, requestid
- Language: TypeScript
- Homepage:
- Size: 626 KB
- Stars: 44
- Watchers: 2
- Forks: 3
- Open Issues: 8
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
- awesome-javascript - @nestjs-steroids/async-context
- awesome-javascript - @nestjs-steroids/async-context
README
Async Context
Zero-dependency module for NestJS that allow to track context between async call
## Installation
```bash
npm install @nestjs-steroids/async-context
yarn add @nestjs-steroids/async-context
pnpm install @nestjs-steroids/async-context
```## Usage
The first step is to register `AsyncContext` inside interceptor (or middleware)
> `src/async-context.interceptor.ts`
```typescript
import { randomUUID } from 'crypto'
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler
} from '@nestjs/common'
import { AsyncContext } from '@nestjs-steroids/async-context'
import { Observable } from 'rxjs'@Injectable()
export class AsyncContextInterceptor implements NestInterceptor {
constructor (private readonly ac: AsyncContext) {}intercept (context: ExecutionContext, next: CallHandler): Observable {
this.ac.register() // Important to call .register or .registerCallback (good for middleware)
this.ac.set('traceId', randomUUID()) // Setting default value traceId
return next.handle()
}
}
```The second step is to register `AsyncContextModule` and interceptor inside main module
> `src/app.module.ts`
```typescript
import { APP_INTERCEPTOR } from '@nestjs/core';
import { Module } from '@nestjs/common';
import { AsyncContextModule } from '@nestjs-steroids/async-context';
import { AsyncContextInterceptor } from './async-context.interceptor';@Module({
imports: [
AsyncContextModule.forRoot()
],
providers: [
{
provide: APP_INTERCEPTOR,
useClass: AsyncContextInterceptor,
},
],
})
export class AppModule {}
```
The last step is to inject `AsyncContext` inside controller or service and use it
> ``src/app.controller.ts``
```typescript
import { Controller, Get, Logger } from '@nestjs/common'
import { AppService } from './app.service'
import { AsyncContext } from '@nestjs-steroids/async-context'@Controller()
export class AppController {
constructor (
private readonly appService: AppService,
private readonly asyncContext: AsyncContext,
private readonly logger: Logger
) {}@Get()
getHello (): string {
this.logger.log('AppController.getHello', this.asyncContext.get('traceId'))
process.nextTick(() => {
this.logger.log(
'AppController.getHello -> nextTick',
this.asyncContext.get('traceId')
)
setTimeout(() => {
this.logger.log(
'AppController.getHello -> nextTick -> setTimeout',
this.asyncContext.get('traceId')
)
}, 0)
})
return this.appService.getHello()
}
}```
## Output example
```
[Nest] 141168 - 02/01/2022, 11:33:11 PM LOG [NestFactory] Starting Nest application...
[Nest] 141168 - 02/01/2022, 11:33:11 PM LOG [InstanceLoader] AsyncContextModule dependencies initialized +47ms
[Nest] 141168 - 02/01/2022, 11:33:11 PM LOG [InstanceLoader] AppModule dependencies initialized +1ms
[Nest] 141168 - 02/01/2022, 11:33:11 PM LOG [RoutesResolver] AppController {/}: +12ms
[Nest] 141168 - 02/01/2022, 11:33:11 PM LOG [RouterExplorer] Mapped {/, GET} route +7ms
[Nest] 141168 - 02/01/2022, 11:33:11 PM LOG [NestApplication] Nest application successfully started +5ms
[Nest] 141168 - 02/01/2022, 11:33:13 PM LOG [7398d3ad-c246-4650-8dd0-f8f29238bdd7] AppController.getHello
[Nest] 141168 - 02/01/2022, 11:33:13 PM LOG [7398d3ad-c246-4650-8dd0-f8f29238bdd7] AppController.getHello -> nextTick
[Nest] 141168 - 02/01/2022, 11:33:13 PM LOG [7398d3ad-c246-4650-8dd0-f8f29238bdd7] AppController.getHello -> nextTick -> setTimeout
```## API
### `AsyncContext` almost identical to native `Map` object
```typescript
class AsyncContext {
// Clear all values from storage
clear(): void;// Delete value by key from storage
delete(key: K): boolean;// Iterate over storage
forEach(callbackfn: (value: V, key: K, map: Map) => void, thisArg?: any): void;// Get value from storage by key
get(key: K): V | undefined;// Check if key exists in storage
has(key: K): boolean;// Set value by key in storage
set(key: K, value: V): this;// Get number of keys that stored in storage
get size: number;// Register context, it's better to use this method inside the interceptor
register(): void// Register context for a callback, it's better to use this inside the middleware
registerCallback(callback: (...args: TArgs) => R, ...args: TArgs): R// Unregister context
unregister(): void
}```
### `AsyncContextModule`
```typescript
interface AsyncContextModuleOptions {
// Should register this module as global, default: true
isGlobal?: boolean// In case if you need to provide custom value AsyncLocalStorage
alsInstance?: AsyncLocalStorage
}class AsyncContextModule {
static forRoot (options?: AsyncContextModuleOptions): DynamicModule
}
```## Migration guide from V1
You need to replace `AsyncHooksModule` by `AsyncContextModule.forRoot()`## License
[MIT](LICENSE.md)