Serverless Nest.js micro-services & integrations without HTTP

Sebastian Schlecht
Dec 29, 2020 · 3 min read
Image for post
Image for post

At Capmo, we are currently evaluating to move our entire backend stack to Nest.js because of its many benefits (which I won’t mention in this blog post as there are plenty of others on that matter).

Since we already have a legacy application running in production, we unfortunately are not looking at a green field here but need to move building blocks piece by piece — among others we have some serverless micro-services that are built using AWS Lambda which we also want to migrate and extend to keep a consistent structure and experience across our backend stack. Those services mainly use native AWS integrations via API Gateway, S3 events or SQS triggers. While there is material on the Internet on how to integrate Nest.js with API Gateway (e.g. here), most approaches rely on a proxy integration between API Gateway and a (cached) express.js or fastify instance. There are ways to interface with other cloud-native integrations using Nest.js micro-services, but many transports don’t come out of the box and we found that wiring up new ones is quite cumbersome and thus were looking for a more straightforward approach that might play nicely with AWS native integrations for Lambda.

Standalone applications to the rescue

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.

The example above illustrates how to wire up a Nest.js app without HTTP overhead directly to Lambda integrations and cache the underlying dependency injection contexts. We plan to use this internally through AWS CDK until Nest.js’ serverless core matures in order to progress our application landscape.

And what’s the downside here?

PS: We are looking for kick-ass software engineers to join our team to shake up the construction industry. If we caught your interest, feel free to get in touch here or directly via Github/LinkedIn.

Capmo Stories

Learnings from building a leading B2B SaaS business in…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store