AWS Lambda With NestJS

Greg Emerick AWS, JavaScript, Node, Technology Snapshot Leave a Comment

Introduction

In my previous blog post, I showed running a Spring Boot Java application in AWS Lambda. I discussed the pros and cons of using Java and Spring with Lambda.

In this blog post, I’ll cover another Lambda option with NestJS. NestJS provides a framework that is not too different from a typical Spring application. It also addresses some of the negatives of using Java and Spring in a Lambda function.

AWS Lambda

To recap, AWS Lambda provides low cost compute with zero maintenance. Lambda runs your code on demand, without provisioned and managed servers. Lambda automatically runs and scales your code. You are charged for every 100ms your code executes and the number of times your code is triggered.

Lambda has clear cost and maintenance benefits over typical on-premise or EC2 deployments. What does it take to run a Nest application as a Lambda? Does NestJS provide benefits over a Java Spring application?

NestJS Introduction

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript that is built with and fully supports TypeScript. Nest attempts to solve the main problem for Node (and server-side JavaScript), by providing architecture. Nest provides an out-of-the-box application architecture which allows developers and teams to create highly testable, scalable, loosely coupled, and easily maintainable applications.

If you are familiar with the application architecture specified by Angular, Nest is a natural adjustment.

Setup Node, NPM, and NestJS

In preparation for creating a Nest application, Node.js (>= 8.9.0) is required. We’ll also need npm. Instructions can be found here.

Nest can now be installed. At the time of this writing, the nest version is 6.1.1. There were some changes to be aware of in version 6.

npm i -g @nestjs/cli

Nest

Let’s get started by creating a new project. If you are impatient, feel free to clone the project ready to go.

nest new nest-lambda
cd nest-lambda

When prompted for the package manager. Choose npm.

Open the new nest-lambda folder in your favorite editor. Visual Studio Code is a great choice. Intellisense for Typescript worked out of the box without any additional plugins. TSLint 1.0.0 (vscode-typescript-tslint-plugin) is recommended.

We can run this project right now and access the default REST service that was created with the new project:

npm run start
curl http://localhost:3000

The output should be: Hello World!

Let’s use the Nest CLI to add a “languages” REST controller. By default, Nest CLI will create a folder for each component. For simplicity sake, we’ll use --flat to write our new controller in the src folder.

nest generate controller languages --flat 

We should now have a languages.controller.ts in the src folder of the project. Notice that it is also added to the controller’s array in the app.module.ts. Let’s modify this controller to return a list of languages by adding a method and a model class:

See Also:  Interactive REST API Documentation with Swagger UI

src/models/language.ts

export class Language {
   name: string;
   constructor(name: string) {
       this.name = name;
   }
}
import { Controller, Get } from '@nestjs/common';
import { Language } from './models/language';

@Controller('languages')
export class LanguagesController {

   @Get()
   languages(): Language[] {
       return [new Language("typescript"), new Language("java")];
   }
}

Test this new endpoint:

npm run start
curl http://localhost:3000/languages

The output should be:

[{"name":"typescript"},{"name":"java"}]

We’ve now built a REST API with TypeScript using the NestJS Framework. Let’s get this thing deployed as a serverless application in AWS.

Lambda With Nest

To run this application as a lambda we first need to add AWS-lambda and AWS-serverless-express npm modules. Then we’ll need to add a new class that can handle API Gateway requests and a CloudFormation deploy script. Check out the lambda branch from the source and review lambda-entry-point.ts and nest-lambda.yaml.

Or add the dependencies as below and download the lambda-entry-point.ts to /src and nest-lambda.yaml. The @types are installed as dev dependencies for improved coding but will be pruned when the deployment package is built.

npm install aws-lambda 
npm install -D @types/aws-lambda
npm install aws-serverless-express
npm install -D @types/aws-serverless-express

The lambda entry point is pretty typical. It caches the application on a cold start and re-uses it for subsequent requests. nest-lambda.yaml is a typical minimal CloudFormation template built with SAM.

Deploy with AWS CloudFormation

AWS CloudFormation provides a common language for describing and provisioning all the infrastructure resources in your cloud environment. The deployment steps will require AWS CLI to be installed and configured.

Before our first deploy, we need to create an S3 bucket to hold the code and CloudFormation template. S3 bucket names must be unique across all users. Be sure to use something unique. The second command adds a lifecycle on the bucket that automatically removes files older than one day. Once the CloudFormation stack is deployed, the files are no longer needed, so there is no reason to keep and pay for them.

aws s3 mb s3://cloudformation-5-4

aws s3api put-bucket-lifecycle --bucket cloudformation-5-4 --lifecycle-configuration '{"Rules":[{"Expiration":{"Days":1},"Status":"Enabled","ID":"Delete1DayOld","Prefix":""}]}'

Build and package our Lambda as a zip then use aws cloudformation to deploy. Note the prune after build to remove the development dependencies. This reduces the deployment package size significantly.

mkdir deploy
npm install
npm run build
npm prune --production
zip -r deploy/nest-lambda.zip dist/ node_modules

aws cloudformation package --template-file nest-lambda.yaml --s3-bucket cloudformation-5-4 --output-template-file deploy/nest-lambda.out.yaml

aws cloudformation deploy --template-file deploy/nest-lambda.out.yaml --stack-name nest-lambda --capabilities CAPABILITY_IAM

The outputs section of the CloudFormation stack will show the URL of the API Gateway that was created. Login to the AWS Console to see this. An example request is below.

curl https://abcdefgh.execute-api.us-east-2.amazonaws.com/Stage/languages

Review and Conclusion

Now, let’s review what we’ve accomplished. We’ve successfully deployed a NestJS server-side application as an AWS Lambda function and executed a REST call against it. We can also test the application locally as we develop it. Nest provides many of the same development conveniences as a Spring Java application.

See Also:  Part 4: Adding Smaller SPAs to An Existing Application

In addition, as a Java developer learning TypeScript, Nest feels very familiar. The dependency injection framework is very similar to that of Spring. The controllers and their decorators are also very similar to Spring REST controllers and annotations.

Where Lambda with Nest really shines over Spring Java is execution time. The graph below shows the maximum execution time of 83 ms. This represents the typical cold start time to service an API request. The minimum request time is 2.54 ms. Each of these is significantly faster than a Lambda written in Java.

Another big plus for Nest is how closely the framework resembles Angular. Nest and Angular have a CLI that enables consistent and rapid development. UI and server-side code can be written in the same language and using very similar frameworks.

I wasn’t able to find many drawbacks to using Nest. All of the Spring Java similarities and noted benefits make Nest worth a look for your Lambda functions.

What Do You Think?