Serverless Nest.js micro-services & integrations without HTTP

By Sebastian Schlecht

When looking at native Lambda integrations however, one might still want to preserve the concept of a request scope. This can be useful for use-cases like preserving native AWS requestIds throughout the lifecycle of an invocation for logging, or pass authorisation headers down to app modules. With standalone apps as described in the guide however, one has only access to singleton scoped providers. What if, for example, we want to share a requestId or userId among all providers within a single invocation / execution context to make logging more transparent, handle authorisation/tenancy, etc.?

In such a case, we can leverage Nest.js’ dependency injection system to create a request-scoped sub-tree for us, which we can use during the life-cycle of a Lambda function invocation. We first instantiate and cache a standalone Nest.js application context, and then create a scoped sub-tree for each invocation:

Subsequently, we can now use request scoped providers throughout our application and inject the { context } object from line 44 through the @Injectdecorator.

@Injectable({ scope: Scope.REQUEST })export class CatsService { constructor(@Inject(REQUEST) private requestContext: Context) {}

}

whereas requestContext in this example is the injected Lambda execution context, but can be arbitrary — for example we can extract headers from the invoking request or event-payload like a userId or tenantId which we can make available to all services through DI.

A note on memory-leaks

Luckily, Nest.js uses WeakMap internally, hence as soon as the reference to contextId (which is an object!) is being garbage-collected, the request-scoped sub-tree will also be garbage-collected subsequently. Details can be found here.