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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kitstack/nest-req-ctx

v1.1.1

Published

Type-safe request context management for NestJS using AsyncLocalStorage with Express and Fastify support

Readme

@kitstack/nest-req-ctx

A type-safe request context management package for NestJS using AsyncLocalStorage. Supports both Express and Fastify adapters.

Features

  • Request Context Storage - Store and access request-scoped data anywhere in your application
  • Type-Safe - Full TypeScript support with generics and type inference
  • Express & Fastify Support - Works with both HTTP adapters out of the box
  • Powerful Decorators - Access request data, headers, and context values via decorators
  • Dot Notation Support - Access deeply nested properties with dot notation (e.g., user.profile.email)
  • Extensible - Create custom context services by extending the base class
  • Multiple Setup Methods - Initialize context via middleware, guard, or interceptor

Installation

Option 1: Interactive CLI (recommended for first-time setup)

From your NestJS project directory, run:

npx @kitstack/nest-req-ctx

The CLI will ask for your preferences (HTTP adapter, setup type, global module, etc.), install the package, and add RequestContextModule to your app.module.ts.

Option 2: Manual install

npm install @kitstack/nest-req-ctx

Quick Start

1. Import the Module

// app.module.ts
import { Module } from "@nestjs/common";
import { RequestContextModule } from "@kitstack/nest-req-ctx";

@Module({
  imports: [
    RequestContextModule.forRoot({
      isGlobal: true, // Makes the module available everywhere
      adapter: "auto", // Auto-detect Express or Fastify
    }),
  ],
})
export class AppModule {}

2. Use Decorators in Controllers

import { Controller, Get } from "@nestjs/common";
import { Req, Header, RequestKey, ContextValue } from "@kitstack/nest-req-ctx";
import { Request } from "express";

@Controller()
export class AppController {
  @Get()
  handler(
    @Req() request: Request,
    @Header("authorization") auth: string,
    @RequestKey("user") user: User,
    @RequestKey<string>("user.email") email: string,
    @ContextValue<string>("tenantId") tenantId: string,
  ) {
    return { user, email, tenantId };
  }
}

3. Inject Context Service

import { Injectable } from "@nestjs/common";
import { RequestContextService, InjectContext } from "@kitstack/nest-req-ctx";

@Injectable()
export class AppService {
  constructor(@InjectContext() private readonly ctx: RequestContextService) {}

  getCurrentUser() {
    return this.ctx.get("user");
  }

  getUserEmail() {
    return this.ctx.getByPath<string>("user.email");
  }
}

Module Configuration

Basic Configuration

RequestContextModule.forRoot({
  isGlobal: true, // Register globally (default: true)
  adapter: "express", // 'express' | 'fastify' | 'auto'
  setupType: "middleware", // 'middleware' | 'guard' | 'interceptor'
  setRequest: true, // Store request object in context
  exclude: ["/health"], // Routes to exclude (middleware only)
  setup: (ctx, req) => {
    // Custom setup function
    ctx.set("requestId", req.headers["x-request-id"]);
  },
});

Async Configuration

RequestContextModule.forRootAsync({
  imports: [ConfigModule],
  useFactory: (config: ConfigService) => ({
    adapter: config.get("HTTP_ADAPTER"),
  }),
  inject: [ConfigService],
});

Decorators

@Req()

Get the full request object:

@Get()
handler(@Req() request: Request) {
  return { path: request.path };
}

@Headers() / @Header()

Get all headers or a specific header:

@Get()
handler(
  @Headers() allHeaders: Record<string, string>,
  @Header('authorization') auth: string,
  @Header('x-request-id') requestId: string,
) {
  return { auth, requestId };
}

@RequestKey()

Get a value from the request object. Supports dot notation:

@Get()
handler(
  @RequestKey('user') user: User,
  @RequestKey('user.email') email: string,
  @RequestKey<string>('user.profile.name') name: string,
) {
  return { user, email, name };
}

@ContextValue()

Get a value from the context store. Supports dot notation and generics:

@Get()
handler(
  @ContextValue('user') user: User,
  @ContextValue<string>('user.email') email: string,
  @ContextValue<string>('tenant.id') tenantId: string,
) {
  return { user, email, tenantId };
}

@InjectContext()

Inject the RequestContextService:

@Injectable()
export class MyService {
  constructor(@InjectContext() private ctx: RequestContextService) {}
}

RequestContextService API

Core Methods

// Set a value
ctx.set("key", value);

// Get a value
const value = ctx.get("key");
const typedValue = ctx.get<User>("user");

// Get value by path (dot notation)
const email = ctx.getByPath<string>("user.profile.email");

// Check if key exists
const hasUser = ctx.has("user");

// Delete a key
ctx.delete("key");

// Get all values
const store = ctx.getAll();

// Check if context is active
const isActive = ctx.isActive();

Request Methods

// Get the request object
const request = ctx.getRequest();

// Get a header
const auth = ctx.getHeader("authorization");

// Get all headers
const headers = ctx.getHeaders();

// Get a request property (supports dot notation)
const user = ctx.getRequestProperty<User>("user");
const email = ctx.getRequestProperty<string>("user.email");

Extending the Context

Create a custom context service for domain-specific functionality:

import { Injectable } from "@nestjs/common";
import {
  BaseRequestContext,
  RequestContextService,
  InjectContext,
} from "@kitstack/nest-req-ctx";

interface AppContextStore {
  user: User;
  tenant: Tenant;
  requestId: string;
}

@Injectable()
export class AppRequestContext extends BaseRequestContext<AppContextStore> {
  constructor(@InjectContext() ctx: RequestContextService<AppContextStore>) {
    super(ctx);
  }

  get currentUser(): User | undefined {
    return this.get("user");
  }

  set currentUser(user: User) {
    this.set("user", user);
  }

  get currentTenant(): Tenant | undefined {
    return this.get("tenant");
  }

  hasRole(role: string): boolean {
    return this.currentUser?.roles.includes(role) ?? false;
  }

  isAdmin(): boolean {
    return this.hasRole("admin");
  }
}

Use it in your application:

@Controller()
export class AppController {
  constructor(private readonly appContext: AppRequestContext) {}

  @Get()
  handler() {
    return {
      isAuthenticated: this.appContext.isActive,
      isAdmin: this.appContext.isAdmin(),
      user: this.appContext.currentUser,
    };
  }
}

Using with Fastify

// main.ts
import { NestFactory } from "@nestjs/core";
import {
  FastifyAdapter,
  NestFastifyApplication,
} from "@nestjs/platform-fastify";
import { AppModule } from "./app.module";

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter(),
  );
  await app.listen(3000);
}

// app.module.ts
@Module({
  imports: [
    RequestContextModule.forRoot({
      adapter: "fastify",
    }),
  ],
})
export class AppModule {}

TypeScript Utilities

Type-Safe Path Access

import { PathsOf, DeepValue } from "@kitstack/nest-req-ctx";

interface MyStore {
  user: {
    id: string;
    profile: {
      email: string;
      name: string;
    };
  };
}

// PathsOf generates all valid paths
type Paths = PathsOf<MyStore>;
// 'user' | 'user.id' | 'user.profile' | 'user.profile.email' | 'user.profile.name'

// DeepValue gets the type at a path
type Email = DeepValue<MyStore, "user.profile.email">; // string

Typed Decorator Factories

import { createTypedContextValue } from "@kitstack/nest-req-ctx";

interface MyStore {
  user: { email: string };
  tenant: { id: string };
}

const TypedContext = createTypedContextValue<MyStore>();

@Controller()
export class AppController {
  @Get()
  handler(
    @TypedContext("user.email") email: string,
    @TypedContext("tenant.id") tenantId: string,
  ) {
    return { email, tenantId };
  }
}

Setting Context Values

Via Middleware

@Injectable()
export class AuthMiddleware implements NestMiddleware {
  constructor(private ctx: RequestContextService) {}

  use(req: Request, res: Response, next: NextFunction) {
    const user = this.validateToken(req.headers.authorization);
    this.ctx.set("user", user);
    next();
  }
}

Via Guard

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private ctx: RequestContextService) {}

  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const user = this.validateToken(request.headers.authorization);
    this.ctx.set("user", user);
    return !!user;
  }
}

Via Module Setup

RequestContextModule.forRoot({
  setup: (ctx, req) => {
    ctx.set("requestId", req.headers["x-request-id"] || uuid());
    ctx.set("correlationId", req.headers["x-correlation-id"]);
  },
});

API Reference

Exports

| Export | Type | Description | | ----------------------- | --------- | ------------------------------------- | | RequestContextModule | Module | Main module with forRoot/forRootAsync | | RequestContextService | Service | Core context service | | BaseRequestContext | Class | Base class for custom contexts | | @Req() | Decorator | Get request object | | @Headers() | Decorator | Get all headers | | @Header(key) | Decorator | Get specific header | | @RequestKey(key) | Decorator | Get request property | | @ContextValue(key) | Decorator | Get context value | | @InjectContext() | Decorator | Inject context service |

Type Exports

| Export | Description | | ----------------------------- | -------------------------------- | | PathsOf<T> | All dot-notation paths of type T | | DeepValue<T, P> | Value type at path P in type T | | UnifiedRequest | Express or Fastify request type | | RequestContextModuleOptions | Module configuration options | | AdapterType | 'express' | 'fastify' | 'auto' |

License

MIT