inversify-express-utils icon indicating copy to clipboard operation
inversify-express-utils copied to clipboard

Request scope doesn't works in express middleware

Open Templaris opened this issue 7 years ago • 2 comments

Expected Behavior

I am using inversify and, inversify-express utlis. I want to use request scoped service in express middleware.

Current Behavior

When I try get service in express middleware like this

export async function expressMiddleware(req: Request, res: Response, next: NextFunction) {

  try {
    ...
 
    let sessionContext = container.get<SessionContext>(TYPES.SessionContext);
    sessionContext.testField = 'test';
    ...
    return next();
  }
  catch (err) {
   next(err);
  }
}

service is resolved. After this I set some test data to service property. Next I am injecting this service in controller or in another class in call stack.

@controller("/test")
export class TestController {
  @inject(TYPES.SessionContext) private sessionContext!: SessionContext;
    
  constructor() { }

  @httpGet("/")
  public test(  
    @next() next: express.NextFunction): Promise<boolean> {
    try {
      console.log(this.sessionContext.testField);
      return true;
    } catch (error) {
      next(error);
    }
  }
}

The problem is that sessionContext service is new instance. How works request scope? I must have same instance in express middleware and controller. What I observed is that scope starts from controller, but should earlier from middleware.

SessionContext is registered manually (I am using inversify-binding-decorators too

import { Container, inject } from "inversify";
import TYPES from "./types";
import { autoProvide, makeProvideDecorator, makeFluentProvideDecorator } from "inversify-binding-decorators";
import { SessionContext } from './../services/session/sessionContext';
let container = new Container();
container.bind<SessionContext>(TYPES.SessionContext).to(SessionContext).inRequestScope();
let provide = makeProvideDecorator(container);
let fluentProvider = makeFluentProvideDecorator(container);

let provideNamed = (identifier: any, name: any) => {
  return fluentProvider(identifier)
    .whenTargetNamed(name)
    .done();
};



export { container, autoProvide, provide, provideNamed, inject };
import { injectable } from 'inversify';
@injectable()
export class SessionContext {
  public testField: string;
}

The same problem is if I use request scope in controller inversify middleware

Templaris avatar Nov 26 '18 11:11 Templaris

This is not an issue. You need to ask the question on stackoverflow. Use an approach that inversify-express-utils provide, make an injectable middleware, follow on the link https://github.com/inversify/inversify-express-utils#basemiddleware

victor-shelepen avatar Dec 11 '18 08:12 victor-shelepen

@vlikin There is an issue with the inversify-express-utils related to Middlewares instantiation, see pull request 235. Read below to discover what is happening.

If I understand correctly it is a problem our company faced 2 months ago:

There is a problem with middlewares instantiation in inversify-express-utils, they are singletons even if the classes are not marked as one. The pull request 235 fix that.

The way the inversify-express-utils Server works is the following: during the build process, the server instantiate all the controllers, services and middlewares to assure the DI is working correctly, but the code of the middleware has a flaw: it only instantiate once durring build and for every request it uses the same instance, making it a singleton as a side effect. If something is a singleton, it does not matter if you rebind something on per request, it will not affect the already created objects.

Also the way the wrapper sets the httpContext on the middleware instance, since it is an unintentional singleton, can make bad things happen: If a middleware is an async function and makes an async operation and another request start the same middleware, the httpContext of the middleware instance is replaced by the new request context. Unless you save the httpContex in a let/const on the top of the handler() function, it has the potential to change during the request and bugs will happen.

In our company we identify this issue and published a package (inversify-express) ourserlves with a fix to that and we made some other changes. Currently the version 0.0.3 of our package does not incude many breaking changes (except for the Pascal Case decorators), mostly fixes, but we are preparing a 0.0.5 that will add some breaking changes and our objective is to launch our own flavor of a wrapper over ExpressJS using inversify by the end of next year (2019).

@Templaris there is a fix to your problem, but you have two options: wait the pull request I mentioned be merged (may take months to that happen) or use my company's forked package and make some changes. In case you opt-in our package, feel free to report feedback or bugs, because we use it in our main products we need to guarantee it works perfectly.

giancarlo-dm avatar Dec 15 '18 18:12 giancarlo-dm