Close

November 27, 2019

NestJS & AWS Lambda Without HTTP

At a current client, we’re looking to move (most of) our AWS Lambda functions to NestJS. The company has built up an extensive collection of Lambda functions and it’s time to bring some structure and similarity in them.

But NestJS is geared towards incoming HTTP calls. This is fine if your Lambda function is behind an API Gateway, but is it possible to use NestJS if your Lambda function should be triggered by SNS events?

Uniformity?

Those who know me, know I’m not a fan of forcing each team and each project in a company to follow the same structure in their code and project organization.

There is never a one-size-fits-all way of organizing code that works for every team. But that’s a whole different discussion.

So why would I be OK with using NestJS for all our AWS Lambda functions? Because it’s just about the framework, not about the details. We’re going to use NestJS, which recommends a certain way of programming. But it doesn’t mean we need to write all our code in the same way. There are even functions that won’t be written with NestJS because they’re so small it would be overkill.

What is NestJS?

NestJS is another JavaScript framework, yes. And while I don’t care for JS framework discussions, it does provide us some great benefits.

Our Lamba functions were previously written in all kind of styles, depending on who wrote it. Often, they weren’t very testable.

NestJS gives us a structure and some guidance that allows for clean code, decoupled components and easier testability.

What’s nice is that is uses Express, which we were already using.

Are there other frameworks out there that provide similar or better benefits? Probably. But NestJS will do the job just nicely.

To HTTP or not to HTTP?

Most of our Lambda functions are triggered by a HTTP call. If you’re not familiar with AWS, you should know that Lambda functions can be started by a variety of triggers: a HTTP call, a record being added to a database, a message being sent to AWS’s Simple Notification Service (SNS),…

In most cases, we use AWS API Gateway, meaning that our Lambda functions are triggered by some HTTP call. The API Gateway then forwards the call to the relevant Lambda function.

However, we have some that are only triggered by other types of events. For example, we have a function that is subscribed to an SNS topic. If you don’t know SNS, think of it as a simple messaging system: someone sends a message to a Topic and other components can subscribe to these topics.

So how can we get NestJS to run without the context of a HTTP call?

NestJS Without HTTP

In “regular” NestJS, you would bootstrap your application and then “listen” for HTTP calls:

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap()

In a Lambda function, you can use the serverless-http package to wrap your NestJS:

async function bootstrap() {
  const app = await NestFactory.create(AppModule, new ExpressAdapter(expressApp));
  return app;
}

// then, in your handler function:
const app = await bootstrap();
const appHandler = serverlessHttp(app);
return await appHandler(event, context);

But that doesn’t work if there won’t be any HTTP calls coming in.

Instead, we can write our Lambda as we would normally and in our handler function we can bootstrap our NestJS application, get the provider we need, and pass on the incoming data:

async function bootstrap() {
  const app = await NestFactory.createApplicationContext(AppModule);
  return app;
}

export async function handler(event, context) {
  const app = await bootstrap();
  const appService = app.get(AppService);
  await appService.doSomething(event);
}

That’s basically it. Instead of having NestJS listen for incoming HTTP calls, we use NestJS for all the other goodies it provides (like dependency injection, separation of concerns and testability) and just get the service we need and pass in the required data.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.