NestJS: How to register transient and per web request providers

I was struggling with similar issue, and one way to achieve this is to use node-request-context module as a global request register, that will give you the request context. So you will not have separate service instances, but you can ask this static register to give you request specific instance/connection.

https://github.com/guyguyon/node-request-context

Create simple context helper:

import { createNamespace, getNamespace } from 'node-request-context';
import * as uuid from 'uuid';

export class RequestContext {

    public static readonly NAMESPACE = 'some-namespace';
    public readonly id = uuid.v4();

    constructor(public readonly conn: Connection) { }

    static create(conn: Connection, next: Function) {
        const context = new RequestContext(conn);
        const namespace = getNamespace(RequestContext.NAMESPACE) || createNamespace(RequestContext.NAMESPACE);

        namespace.run(() => {
            namespace.set(RequestContext.name, context);
            next();
        });
    }

    static currentRequestContext(): RequestContext {
        const namespace = getNamespace(RequestContext.NAMESPACE);
        return namespace ? namespace.get(RequestContext.name) : null;
    }

    static getConnection(): Connection {
        const context = RequestContext.currentRequestContext();
        return context ? context.conn : null;
    }

}

The conn instance parameter is your connection, feel free to put there other request specific dependencies. Also the id there is just for debugging, no real need to use uuid module as I did.

Create middleware wrapper (this allows you to use DI here):

@Injectable()
export class ContextMiddleware implements NestMiddleware {

  constructor(private readonly connectionManager: ...) { }

  resolve(...args: any[]): MiddlewareFunction {
    return (req, res, next) => {
      // create the request specific connection here, probably based on some auth header...
      RequestContext.create(this.connectionManager.createConnection(), next);
    };
  }

}

Then register new middleware in your nest application:

const app = await NestFactory.create(AppModule, {});
app.use(app.get(RequestLoggerMiddleware).resolve());

And finally the profit part - get the request specific connection anywhere in your application:

const conn = RequestContext.getConnection();

With the release of nest.js 6.0, injection scopes were added. With this, you can choose one of the following three scopes for your providers:

  • SINGLETON: Default behavior. One instance of your provider is used for the whole application
  • TRANSIENT: A dedicated instance of your provider is created for every provider that injects it.
  • REQUEST: For each request, a new provider is created. Caution: This behavior will bubble up in your dependency chain. Example: If UsersController (Singleton) injects UsersService (Singleton) that injects OtherService (Request), then both UsersController and UsersService will automatically become request-scoped.

Usage

Either add it to the @Injectable() decorator:

@Injectable({ scope: Scope.REQUEST })
export class UsersService {}

Or set it for custom providers in your module definition:

{
  provide: 'CACHE_MANAGER',
  useClass: CacheManager,
  scope: Scope.TRANSIENT,
}

Outdated Answer

As you can see in this issue, nestjs does not yet offer a built-in solution for request-scoped providers. But it might do so in the near future:

Once async-hooks feature (it is still experimental in node 10) is stable, we'll think about providing a built-in solution for request-scoped instances.