You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NestJS服务中获取Interceptor注入的数据?

Solution for Sharing Transaction Data from RabbitMQ Interceptor to Services in NestJS

When working with NestJS + GolevelUP/RabbitMQ, request scope doesn’t always behave as expected because RabbitMQ message contexts aren’t tied to Nest’s default HTTP request scope. Here are two reliable ways to share your interceptor-computed data across services:

1. Use AsyncLocalStorage (ALS) for Context Propagation

ALS is a Node.js feature that preserves context across async operations, making it ideal for sharing data between interceptors and services without scope changes.

Step 1: Create an ALS Context Service

This service wraps AsyncLocalStorage to manage your transaction data:

import { Injectable } from '@nestjs/common';
import { AsyncLocalStorage } from 'async_hooks';

@Injectable()
export class MessageContextService {
  private readonly als = new AsyncLocalStorage<Map<string, any>>();

  // Run a callback within the context
  runWithContext<T>(contextData: Map<string, any>, callback: () => T): T {
    return this.als.run(contextData, callback);
  }

  // Retrieve data from context
  get(key: string): any {
    const store = this.als.getStore();
    return store?.get(key);
  }

  // Add data to context
  set(key: string, value: any): void {
    const store = this.als.getStore();
    if (store) store.set(key, value);
  }
}

Step 2: Update Your Interceptor to Use ALS

Populate the context before executing the message handler:

import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { MessageContextService } from './message-context.service';

@Injectable()
export class RabbitTransactionInterceptor implements NestInterceptor {
  constructor(private readonly msgContext: MessageContextService) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    // Extract RabbitMQ context and compute your transaction data
    const rabbitCtx = context.switchToRpc().getContext();
    const transactionData = { /* your computed data here */ };

    // Store data in ALS context
    const contextMap = new Map<string, any>();
    contextMap.set('transactionData', transactionData);

    // Execute handler within the context
    return this.msgContext.runWithContext(contextMap, () => next.handle());
  }
}

Step 3: Access Data in Your Services

Inject the MessageContextService to retrieve the data:

import { Injectable } from '@nestjs/common';
import { MessageContextService } from './message-context.service';

@Injectable()
export class ServiceInjected1 {
  constructor(private readonly msgContext: MessageContextService) {}

  someCall(): void {
    const transactionData = this.msgContext.get('transactionData');
    // Use the data as needed
  }
}

2. Request-Scoped Context Provider (Nest-Native Approach)

If you prefer using Nest’s built-in scoping, ensure your context is tied to each RabbitMQ message by marking relevant providers as request-scoped.

Step 1: Create a Request-Scoped Context Service

import { Injectable, Scope } from '@nestjs/common';

@Injectable({ scope: Scope.REQUEST })
export class TransactionContext {
  transactionData: any; // Holds your computed data
}

Step 2: Populate Context in the Interceptor

import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { TransactionContext } from './transaction-context.service';

@Injectable()
export class RabbitTransactionInterceptor implements NestInterceptor {
  constructor(private readonly txContext: TransactionContext) {}

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const rabbitCtx = context.switchToRpc().getContext();
    this.txContext.transactionData = { /* your computed data */ };
    return next.handle();
  }
}

Step 3: Mark Your RabbitMQ Controller as Request-Scoped

This ensures a new TransactionContext instance is created per message:

@Controller()
@Injectable({ scope: Scope.REQUEST })
export class RabbitController {
  constructor(private readonly serviceInjected1: ServiceInjected1) {}

  @MessagePattern('your-queue-name')
  async handleMessage(message: any) {
    await this.serviceInjected1.someCall();
  }
}

Step 4: Access Data in Services

Inject the TransactionContext directly:

import { Injectable } from '@nestjs/common';
import { TransactionContext } from './transaction-context.service';

@Injectable()
export class ServiceInjected2 {
  constructor(private readonly txContext: TransactionContext) {}

  someCall(): void {
    const data = this.txContext.transactionData;
    // Use the data
  }
}

Key Notes

  • ALS Pros: Lightweight, no scope changes, avoids performance overhead of request-scoped providers.
  • Request-Scoped Pros: Uses Nest's native DI system, easier to understand for those familiar with Nest scoping.
  • Pitfalls: For ALS, ensure all async operations related to the message run within the runWithContext callback. For request-scoped, be aware of potential performance impacts with high message volumes.

内容的提问来源于stack exchange,提问作者Patrick Acioli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 06:03:21