Skip to content

Instantly share code, notes, and snippets.

@bengry
Created December 26, 2019 07:32
Show Gist options
  • Save bengry/924a9b93c25d8a98bffdfc0a847f0dbe to your computer and use it in GitHub Desktop.
Save bengry/924a9b93c25d8a98bffdfc0a847f0dbe to your computer and use it in GitHub Desktop.
Nest.js request context workaround
import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common";
import { APP_INTERCEPTOR } from "@nestjs/core";
import { RequestContextMiddleware } from "./request-context/request-context.middleware";
import { RequestContextModule } from "./request-context/request-context.module";
@Module({
imports: [
ConfigModule.load(path.resolve(__dirname, "config", "**/!(*.d).{ts,js}")),
WebappUsersModule,
RequestContextModule,
LoggerModule,
],
...
})
export class AppModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(RequestContextMiddleware).forRoutes("*");
}
}
import { Injectable, NestMiddleware } from "@nestjs/common";
import { Request, Response } from "express";
import { RequestContext } from "./request-context.model";
/**
* This is needed to side-step Nest.js, which doesn't support getting the current execution context (i.e. Request) that's
* not from the Controller handles directly (and passing it down explicitly). This means that things like a Logger can't
* use DI to get the current user (if any).
*
* This solution is taken from https://github.com/nestjs/nest/issues/699#issuecomment-405868782.
*/
@Injectable()
export class RequestContextMiddleware implements NestMiddleware<Request, Response> {
use(req: Request, res: Response, next: () => void) {
const requestContext = new RequestContext(req, res);
RequestContext.cls.setContext(requestContext);
next();
}
}
import { ContinuationLocalStorage } from "asyncctx";
import { Request, Response } from "express";
export class RequestContext {
static cls = new ContinuationLocalStorage<RequestContext>();
static get currentContext() {
return this.cls.getContext();
}
readonly requestId: number;
constructor(public readonly req: Request, public readonly res: Response) {
this.requestId = Date.now();
}
}
import { Module } from "@nestjs/common";
import { RequestContextMiddleware } from "./request-context.middleware";
import { RequestContextService } from "./request-context.service";
@Module({
providers: [RequestContextMiddleware, RequestContextService],
exports: [RequestContextMiddleware, RequestContextService],
})
export class RequestContextModule {}
import { Injectable } from "@nestjs/common";
import { IUserDocument } from "../modules/webapp-users/interfaces";
import { RequestContext } from "./request-context.model";
@Injectable()
export class RequestContextService {
get currentUser(): IUserDocument | null {
const requestContext = this.currentRequest;
return (requestContext && requestContext.req.user) || null;
}
get currentRequestId(): number | null {
const requestContext = this.currentRequest;
return (requestContext && requestContext.requestId) || null;
}
private get currentRequest() {
const requestContext = RequestContext.currentContext;
return requestContext || null;
}
}
// This can be done anywhere. In my case I wanted to get the current user and requestId
import { Injectable } from "@nestjs/common";
import { IUserDocument } from "../modules/webapp-users/interfaces";
import { RequestContext } from "./request-context.model";
@Injectable()
export class RequestContextService {
get currentUser(): IUserDocument | null {
const requestContext = this.currentRequest;
return (requestContext && requestContext.req.user) || null;
}
get currentRequestId(): number | null {
const requestContext = this.currentRequest;
return (requestContext && requestContext.requestId) || null;
}
private get currentRequest() {
const requestContext = RequestContext.currentContext; // this is the actual usage.
return requestContext || null;
}
}
@NewEXE
Copy link

NewEXE commented Jul 5, 2023

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment