Deploying Serverless API with Nestjs and AWS CDK

Rushi Patel
NextFaze
Published in
9 min readSep 4, 2020

What this article is about

Javascript has evolved a lot in recent years and its ecosystem is bigger today than it was ever before. We are seeing more and more frameworks coming out everyday that help us write more clean, robust and highly manageable code. In this article we will mainly be looking at technologies and tools that can help us build secure, scalable, reliable, efficient, and affordable API.

we will mainly be looking two technologies: AWS CDK and NestJS.

Let’s cover some basic ground on CDK (Cloud Development Kit) and NestJs, so everyone is on the same page.

Our goal here is to build a server-less API without losing tools that we already know and like to work with. You should have some working knowledge of building serverless applications on cloud and why go serverless as this article does not explore reasons to create serverless api.

What is AWS CDK and why use it

AWS CDK a.k.a Cloud Development Kit is a new addition to AWS’s Infrastructure as code initiative, to help developers provision resources on cloud with the languages that they like. You can read more about it here as we won’t be going into the details of how it works. Instead we will have look at how to get a demo application up and running. 👨‍💻

AWS CDK also comes with a CLI tool which makes development and deployment experience very smooth.

What is NestJs

A progressive Node.js framework for building efficient, reliable and scalable server-side applications. — Nestjs

NestJs is a framework that enables developers to take advantage of Angular style Dependencies Injection (DI) for building highly maintainable APIs. It is built with Typescript and combines elements of Object Oriented Programming, Functional Programming and Functional Reactive Programming.

NestJs has a CLI to help developers quickly get started and also takes care of inferring types for swagger template without having to manually annotate each operation.

But why run NestJs on lambda?

Now you might be wondering why you would want to run a NestJs application on a Lambda, If not, you can skip this section.😉

If you have previously done some Serverless applications than you might already know how much of a blessing a Serverless application is over traditional ones that requires a server (On premises and Cloud) constantly running even though no one is using it. This is especially true when you are a startup or have an unpredictable workload. Building Serverless API can be hard as most of the frameworks and practices that we have developed in past many years simply do not work as is, instead we have small executable functions where we write a business logic and then configure it to run on some event or an operation. This means we need to create resource for each resource, and even each operation, not to mention the various limits set by cloud providers (AWS, Azure) when we try to do this. (i.e AWS cloudformation has a max limit of 200 resources per a template).

By running NestJs application on lambda we can try to get best of both world, We get reliability, and affordability and security from AWS world and scalability and better overall better development experience from NestJs land.

I hope I have convinced most of you on why to run NestJs application on lambda. Now let’s first have a look at what we will be building today and then jump straight into coding.

What we are going to build

  • API with CRUD operations for todos
  • User Authentication using Cognito

Local Environment Prerequisites

Getting Started With Setting up Project

Bootstrap a cdk project

npx cdk init --language typescript

This will install all the necessary tooling required to deploy our app to the cloud, because CDK often actively gets new updates, it is possible that by the time you are reading this article AWS CDK might have a new latest version then the one bootstrapped with above command, to update all the dependencies run and follow onscreen commands.

npx npm-check-updates -u

Next, bootstrap nestjs project with name api

nest new api --skip-git

Now we have everything that we will need to create our first serverless API

Configuring required resources for cloud ☁️️

Before going further make sure you are in a root directory of the project, since all the dependencies that we will install now will only be required at the deployment time.

Adding User Authentication using AWS Cognito User Pools

Create a new Cogito construct that extends base CDK Constrct which will contain all of our auth resources.

Create Cognito Construct

add user pool to store user sessions

First, we will configure user pool as following

add user pool client which will allow api users to get auth tokens on login

Next, we will add user pool client for earlier user pool

Configure hosted OAuth server

Finally, we will add a hosted OAuth server to our Cognito construct.

Instead of using Cognito hosted UI, we can also other auth flows that AWS Cognito supports but that is out of scope for the purpose of this article. So for this demo we will just stick to using cognito hosted ui.

Don’t forget to change domainPrefix to be of your choice, as there can only be one app with any given name

Now we are all set for user signup/login, next let’s have a look at configuring Amazon API Gateway resource and AWS Lambda.

Adding API Gateway proxy lambda resource

Our Authentication service is configured, now we are going to add API gateway resource with backed by lambda function.

Configure Lambda function and lambda layer

First, add a lambda function to handle all incoming api requests. When AWS invokes your lambda function in response to configured events, it expects all of the runtime dependencies to be available next to application code.To satisfy this requirement and to also keep out application logic from its dependencies, we will use lambda layers.

Layers allows for easy sharing of external dependencies and greatly improves development experience, as all of dependencies live in a seperate directory that which gets packed and deployed separately to actual business logic.

Also make sure to exclude node_modules dir from code asset.

Configure API Gateway resource

Next, we will add an api gateway resource to respond to all incoming http connections and an api key with usage plan configured.

Usage plan and API key allow us to enable API monitoring.

Configuring api key and usage plan is completely optional and can be skipped, if you do not with to have api monitoring.

Create Dynamo DB table to store our todos

Enable Cognito Authentication

Finally, we will be adding cogito authorizer to authenticate incoming requests. We want to eliminate all bad traffic with no or bad tokens long before it ever gets to our lambda function.

AWS CDK is very new and continuously evolving so it might not have the all the services available as a higher level constructs but you can always use low level Cfn Constructs. At the time of writing this article, aws cdk does not have a construct to create a cognito authorizer so to work around that we will be using low level CfnAuthorizer.

This is all that we need in order to be able to deploy our awesome serverless api. In the next section, we will look into how to make Nestjs respond to different endpoints and some neat things that you can do using Nestjs.

If you made it this far, congrats! Give your self a small break and come back when you are ready for more.

Going wild with Nestjs

Adding serverless capability to our Nestjs application

Updating app listener to respond to lambda invocation

In our main.ts, change the cli generated function with following

Here, we are simply exporting named function ‘api’, which if you remember was set as an entry point in api handler.

Add todos controller

Controller in Nestjs are responsible for managing incoming requests and responses and it is the only piece of code that directly gets mounted onto a route.

First we will add todos controller using nest cli, to do that navigate to /api directory and execute following command.

bootstrap initial controller files using cli

nest generate controller todos

this will create a todos controller and register it in app module.

Next, we will create Create, Read, Put, Delete endpoints

This is the brief of what each endpoints needs to do.

Create: creates a todo item with given data for user

Read: Get all todos and by id

Update: Update item with given id

Delete: delete all delete an item by id and

Deploying

We will use CDK cli to deploy our application to AWS. Make sure you have AWS credential configured on your machine with required permissions.

If you have never used CDK cli to deploy application, then there will be one additional step required before actually deploying our stack, we will need to deploy CDK toolkit stack to deploying region, so that CDK cli can attach and find metadata that it creates.

To Bootstrap CDK toolkit stack, run

npm run cdk bootstrap

then, to deploy our awesome serverless API simply run

npm run cdk deploy

CDK cli will use your default aws credentials configured with aws cli. Incase you have more than one profiles set up you can choose specific one by appending profile flag .

npm run cdk deploy -- --profile <profileName>

That is it, if there were no deploy time errors, you should have a Serverless API that is secure, scalable, reliable, efficient, and affordable, just as we talk about.

Testing

Head over to AWS console and then to a region that our resources were created. Next navigate to AWS Cognito and look for Lauch Hosted UI under App Client settings

Click on the Hosted UI and login/register a user. Once logged in you will be navigated to https://www.google.com if following above example (If you remember correctly, this is what we had setup as a callback url in Adding User Authentication using AWS Cognito User Pools section), and locate tokens(mainly id_token and access_token) in query params, note them somewhere as we will need this later for invoking the API.

Next in a different tab, open up API Gateway page,

then under APIS > Stages > v1 and copy the invoke URL (yes this is our deployed api url) and API key from APIS > API Keys > key.

That’s everything we need, now open up Postman or any REST api client and invoke our API. Don’t forget to try all GET, POST, PUT and DELETE operations!

i.e With cURL it will look something like this

curl --location --request GET 'https://{api-id}.execute-api.{region}.amazonaws.com/v1/users/{userId}/todos' \--header 'x-api-key: <Your API Key>' \--header 'Authorization: Bearer <Your Id Token>'

Where to next…

Complete working example is available on github.

In the next article we will look at using NestJs with non-http AWS services, like invoking a lambda to respond to some Cloudwatch event or an S3 upload. In addition to this, we will also have a look at adding live documentation to our Serverless API (no more need to manage documentation manually 🤓) and generating client SDK(Software Development Kit) using Swagger code gen .

Happy Serverlessing!

Thanks for reading! If you liked this article, hit that clap button below👏 . It means a lot to me and it helps others more easily find this article. For more updates follow us on Medium or follow me on Github.

--

--