npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@nestjs-steroids/async-context

v2.0.0

Published

NestJS Async Context based on async_hooks

Downloads

22,516

Readme

Zero-dependency module for NestJS that allow to track context between async call

Installation

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

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<string, any>) {}

  intercept (context: ExecutionContext, next: CallHandler): Observable<any> {
    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

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

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<string, string>,
    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

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<K, V>) => 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<R, TArgs extends any[]>(callback: (...args: TArgs) => R, ...args: TArgs): R

  // Unregister context
  unregister(): void
}

AsyncContextModule

interface AsyncContextModuleOptions {
  // Should register this module as global, default: true
  isGlobal?: boolean

  // In case if you need to provide custom value AsyncLocalStorage
  alsInstance?: AsyncLocalStorage<any>
}

class AsyncContextModule {
  static forRoot (options?: AsyncContextModuleOptions): DynamicModule
}

Migration guide from V1

You need to replace AsyncHooksModule by AsyncContextModule.forRoot()

License

MIT