-
-
Save bengry/924a9b93c25d8a98bffdfc0a847f0dbe to your computer and use it in GitHub Desktop.
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; | |
} | |
} |
This won't work if multiple requests come in at the same time, and your code has any asynchronized processing.On further reading, this seems to be a working code. Thanks for sharing it! Reference: https://medium.com/@siddiqr67/async-hooks-the-coolest-way-to-save-execution-context-676d8af57f73
Hell yeah I got the exact same consideration…
I give more intel from your article:
Data can be shared between async context thanks to asyncctx
library which is using async a experimental feature of node at this time I'm writing.
Now you can replace asyncctx with the new (Node12) API async local storage
They allow storing data throughout the lifetime of a web request or any other asynchronous duration. It is similar to thread-local storage in other languages.
This snippet can be replace by this library that use the "new" API : https://github.com/abonifacio/nestjs-request-context
For now you can use https://github.com/Papooch/nestjs-cls
@kieranongh Hi! I just want to help you that problem. You can inject your request-context service to subscriber or service (concise place that you want) and use it directly.