-
-
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; | |
} | |
} |
Thanks for your solution!
But I can not realize it in my project, because in request-context.service requestContext
is undefined: https://github.com/noluckjustskill/nestjs-graphql-multitenants/blob/5d32ff42b7c3862c77304c8582afde74a58eabb5/src/tenants/tenants.service.ts#L23
Because currentId
of static instance of ContinuationLocalStorage
are difference in middleware and in service
P.S node.js version is 14.8.0
request-context.service.ts
and some-service.ts
appear to be the same files
Just wondering what an example of some-service.ts
would look like
@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.
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
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