Help needed with request scope
Expected Behavior
I expect easy access to request scope bound variables or at least some help in this issue.
Current Behavior
Little hard to get it working in some frameworks. Express based frameworks have a problem with code binding types in middlewares as with few clever timeouts we can create race conditions. Example below...
Possible Solution
I have made a simple demo using Hapi with child containers instead of requestScope. https://github.com/zveljkovic/hapi-inversify-request-scope
Steps to Reproduce (for bugs)
I didn't quite understand the solution for accessing the request based data as explained in https://github.com/inversify/InversifyJS/issues/381#issuecomment-342145761 so if you can please comment on the code in https://github.com/zveljkovic/hapi-inversify-request-scope on best practices for this kind of requirement
Context
When building with express based libraries (typescript-rest) I need easy way to extract some request information and use it inside controllers, services, repositories. Examples are requestTraceId, userContext, or tenantModel in multitenant app. Tenant is best example as it is figured out in middlewares before controllers and currently I assign it to request object. In controller i pass it (tenant) to service, then service calls repository function and passes it again. It would be best to have a DI framework do all of the instantiation and passing it around.
Failing example - race condition
InitializeRequestDataMiddleware.ts
export async function InitializeRequestDataMiddleware(req: any, res: Response, next: NextFunction) {
req.traceId = Utils.generateTrackingId('TR');
console.log('Rebinding RequestTraceId to ', req.traceId);
if (myContainer.isBound(TYPES.RequestTraceId)) {
myContainer.rebind(TYPES.RequestTraceId).toDynamicValue((context) => req.traceId).inRequestScope();
} else {
myContainer.bind<string>(TYPES.RequestTraceId).toDynamicValue((context) => req.traceId).inRequestScope();
}
setTimeout(() => { next(); }, 5000); // NOTE #1
}
HomeController.ts
@Path('/')
@injectable()
export class HomeController { // NOTE #2
public requestTraceId: string;
constructor(@inject(TYPES.RequestTraceId) requestTraceId: string,) {
this.requestTraceId = requestTraceId;
}
@Get
public async home(): Promise<{status: string, quote: string, test: any, zest: string}> {
console.log('Before timeout', this.requestTraceId);
return new Promise<{test: any}>((resolve, reject) => {
setTimeout(() => {
console.log('After timeout', this.requestTraceId); // NOTE #3
resolve({
test: this.requestTraceId,
});
}, 2000);
});
}
}
Note 1: I have put this timeout to represent some delays in some other middlewares before controller action is activated. This also helps me to launch few requests in parallel. Note 2: Controller gets resolved after middlewares next function is called Note 3; This log will show duplicated requestTraceId if there are two paralel requests. First req will assign value to global container and stop at first timeout (from Note #1). 2nd req will override the value and stop at same timeout. Then both controllers are afterwards initiated with the value from the 2nd request.
I"m trying to get request scope as well but without success. Thought it was just .inRequestScope() like doc says
I have the same problem. The example from the documentation doesn't seem to work. Any updates?