DEV Community

Cover image for How to deploy a Node.js application to AWS Lambda using Serverless
Adnan Rahić
Adnan Rahić

Posted on • Edited on

How to deploy a Node.js application to AWS Lambda using Serverless

Being a developer is amazing. Writing code, solving problems and thinking of ingenious solutions for complicated algorithms is what we live for. But, the grass is not always so green on this side of the fence. Sooner or later you need to get your hands dirty and deploy the app you worked so hard on. Deployments are not always easy. To be blunt, they can be incredibly hard and time-consuming. That's what we'll solve in this tutorial.

TL;DR

You can follow along from the beginning or jump to the section which interests you most, and severely hurt my feelings. 😅

Goals

Today you'll learn how to deploy a Node.js application to AWS Lambda with the help of the Serverless Framework.

The walkthrough will also cover a real-life scenario of deploying a production version of your application, with environment variables.

Prerequisites

This tutorial will need you to have a few simple things set up already. Don't worry, nothing special, just the basics, and I'll link it all below for you to see, and make sure you have, before moving on.

Ready? Let's go!

What's AWS Lambda?

AWS Lambda is a pay-as-you-go serverless compute service. Meaning what now? Well, you just deploy your source code to AWS and they handle the rest. Amazing! No need to fiddle with servers, ssh connections, Linux or Vim. But, want to know what's even better? It scales automatically and has absolutely no downtime. I'll let that sink in...

The technical definition for AWS Lambda would be a Function as a Service. You deploy some code, it gets invoked, processes some input, and returns a value. Simple!

Hold up a sec. We need to mention a crucial fact. All lambda functions are stateless, meaning they cannot store persistent data. And, if I just said Function as a Service, how do we deploy a whole Node.js application to AWS Lambda?

But, how does it work?

When you think about it, it's not that complicated. A single lambda function is essentially just a tiny Node.js runtime environment. You can run whatever you want in there. That's what we'll do, package up a Node.js app and send it off to AWS Lambda. Sneaky. 😉

What will we deploy?

To make this example as simple as possible, the code we'll be deploying is just 7 lines long. However, it could be as large as any Node.js application you have in production, and it would all work like a charm. Intriguing...

How do we deploy it?

In comes the Serverless Framework like a horse riding a white knight! Hold up, I may have that backward. 🤔

Anyhow, this awesome framework enables us to both develop our apps locally, just like we're used to, but also deploy it with one simple command. Hmm... tell me more.

Let's get coding!

First thing's first. Open up your terminal, we need to install some packages. Yay, installing stuff, love it! 😫

1. Install and configure the Serverless Framework

$ npm install -g serverless
Enter fullscreen mode Exit fullscreen mode

Note: Prefix the command with sudo if you're running this command on Linux.

$ sls config credentials --provider aws --key PUBLIC_KEY --secret SECRET_KEY
Enter fullscreen mode Exit fullscreen mode

Make sure to add your IAM User's public and secret key instead of the placeholders I specified above. If you skipped this part above, here's the official guide in the AWS docs.

2. Create the boilerplate code

Let's create a new folder and give it a rather noteworthy name. Jump over to your terminal and run the commands below.

$ mkdir serverless-nodejs-app && cd serverless-nodejs-app
Enter fullscreen mode Exit fullscreen mode

Awesome, now what's left is to run the create command to generate some starter code for us. This is called a serverless service.

$ sls create -t aws-nodejs -n serverless-nodejs-app
Enter fullscreen mode Exit fullscreen mode

Only one more step before opening a code editor.

3. Installing dependencies

Like you can see in the code snippet above, we need to install a few modules first. Lucky for us there are only two and it's as simple as running one command.

$ npm init -y
$ npm install --save express serverless-http
Enter fullscreen mode Exit fullscreen mode

That's it! Let's open it up in a code editor and do some real coding.

4. Coding for real

Once you open up the code editor, you'll see three files. Ignoring the .gitignore file, let me explain what the handler.js is first, then I'll move on to the serverless.yml. The handler will hold all your app logic, all the code. While the servereless.yml is the configuration file for the resources you'll be creating on AWS.

vs code sidebar shows the file structure

Go ahead and rename the handler.js to app.js, just to make it simpler for us to figure out what goes where.

Delete all the starter code and paste this code snippet into the app.js file.

// app.js

const express = require('express')
const sls = require('serverless-http')
const app = express()
app.get('/', async (req, res, next) => {
  res.status(200).send('Hello World!')
})
module.exports.server = sls(app)
Enter fullscreen mode Exit fullscreen mode

Seven lines of code 😎. Looks familiar right? Just like you're used to. That's it. Believe it or not, there's nothing more to it. Let's move on to the serverless.yml.

Once again, delete all the boilerplate code and paste this in.

# serverless.yml

service: serverless-nodejs-app

provider:
  name: aws
  runtime: nodejs8.10
  stage: dev
  region: eu-central-1

functions:
  app:
    handler: app.server # reference the file and exported method
    events: # events trigger lambda functions
      - http: # this is an API Gateway HTTP event trigger
          path: /
          method: ANY
          cors: true
      - http: # all routes get proxied to the Express router
          path: /{proxy+}
          method: ANY
          cors: true
Enter fullscreen mode Exit fullscreen mode

Done! All that's left is to deploy it.

Ready to deploy!

Switch back to the terminal window. By running one simple command your app will be deployed.

$ sls deploy
Enter fullscreen mode Exit fullscreen mode

The Serverless Framework will now wrap everything up into a nice bundle, create a CloudFormation file from the serverless.yml and shoot it off to AWS S3. Once the resources are created and the code is deployed, you'll see an endpoint get sent back to you in the terminal.

terminal show sls deploy output

Opening up the provided URL in a browser you'll see Hello World! get sent back to you.

Deploying to production!

This is great and all, but not really ready for a production environment... yet. Worry not! You'll be surprised how simple it is to make it production ready.

1. Add a secrets.json file to keep environment variables

For now, let's just add the NODE_ENV in the secrets.json.

{
  "NODE_ENV": "production"
}
Enter fullscreen mode Exit fullscreen mode

2. Add a reference for the secrets.json in the serverless.yml

As simple as it was to add the secrets file, it's even easier to just reference the file in the serverless.yml.

service: serverless-nodejs-app

custom: # add these two lines
  secrets: ${file(secrets.json)} # reference the secrets.json file

provider:
  name: aws
  runtime: nodejs8.10
  stage: production # make sure to change this to production
  region: eu-central-1
  environment: # add environment property
    NODE_ENV: ${self:custom.secrets.NODE_ENV} 
    # reference the NODE_ENV from the secrets.json file

functions:
  app:
    handler: app.server
    events:
      - http:
          path: /
          method: ANY
          cors: true
      - http:
          path: /{proxy+}
          method: ANY
          cors: true

Enter fullscreen mode Exit fullscreen mode

Amazing, that's it! Delete the node_modules and .serverless folders from the service and run npm install once again, but this time with the --production flag.

$ npm install --production
Enter fullscreen mode Exit fullscreen mode

Great! All that's left is to re-deploy the service and you're set.

$ sls deploy
Enter fullscreen mode Exit fullscreen mode

And this is what we end up with.

vs code final file structure

I guess we're done? Not really. Having an app running in production just because you installed npm modules with --production doesn't really cut it. To be able to sleep well at night, I need a bit more. Here's where proper system insight and monitoring tools come to play. Let me show you.

How to gain insight into your system?

The #1 problem with all serverless applications are their distributed nature. Plain and simple, it's impossibly hard to have an overview of all the things going on. Not to mention how hard it is to debug when something goes wrong.

To calm my fears I use Tracetest. It gives you end-to-end testing & debugging powered by OpenTelemetry.

Thankfully, there's detailed documentation, which makes the onboarding process a breeze. Go ahead and follow the Quick Start guide. Don't forget to come back here though. 😄

Wrapping up

This was fun!

Lambda is awesome! When combined with HTTP event triggers such as API Gateway, development tools like the Serverless Framework and observability-based testing tools such as Tracetest, things just become so easy.

This simple API example we coded above is just a proof of concept. But you can see the point. It gives you a starting point from where you can create awesome production apps!

If you missed any of the steps above, here's the repository with all the code.

Feel free to join my serverless newsletter if you want to play a key role in the serverless revolution!

Or, take a look at a few of my other articles regarding serverless:

Hope you guys and girls enjoyed reading this as much as I enjoyed writing it. Until next time, be curious and have fun.


Disclaimer: Tracetest is sponsoring this blogpost. Use observability to reduce time and effort in test creation + troubleshooting by 80%.


Top comments (16)

Collapse
 
harrylincoln profile image
Harry Lincoln

Hey Adnan, just something to add to your banging tutorial...

There's a bit of a CORS issue with my setup on the express side, regardless of what you set in the API gateway.

The final step was to make sure that Express could handle it with a cheeky include of this:
npmjs.com/package/cors

Collapse
 
vittoriogassman profile image
Víctor Liendo

Hello Adnan. Reading this at the beginning: "However, it could be as large as any Node.js application you have in production, and it would all work like a charm".

This could be exactly my case. I have been developing a backend using NodeJs, TypeORM, TypeScript, PostgreSQL and Typeorm. Could an entire backend project work as a LAMBDA function ? I have coded about 10%, desployed it as a single LAMBDA function and for now it works ok, but I'm afraid about how the node_modules is growing, because typeORM, sharp and aws-sd are big in size ...

Collapse
 
adnanrahic profile image
Adnan Rahić

Hey Victor! AWS Lambda does has its limits. Read more here. Long story short your deployment package, the .zip file needs to be under 50mb when you're uploading it directly. You can bypass this by uploading your .zip from S3 to Lambda. In that case the limit is 250mb. Read more here.

But, my advice for you here would be to create Lambda functions as microservices. Separate the logic within the application so you can distribute everything across multiple functions. Only use dependencies in places where they are needed, not across all functions. And, keep in mind AWS SDK is included in Lambda, you don't need to explicitly install it in the node_modules.

Check this tutorial out. It can help you understand how to structure your functions.

Good luck, and happy coding. :)

Collapse
 
vittoriogassman profile image
Víctor Liendo

Thanks buddy. Your suggestions have been very useful. First thing i did was removing aws-sdk from node-modules and every thing has gone ok. Second i have just started breaking my backend into smaller parts proxied through the same API Gateway in AWS ... Everything alright so far. Thank you very much

Collapse
 
piyushgargdev profile image
Piyush Garg

Hey there, Nice Post. I made an npm package for handling AWS Lambda responses. Do checkout 😎

AWS-TS This package lets you handle and send responses from AWS lambda with ease. You have the ability to send various types of responses such as JSON or Plain Text without worrying about headers and status codes. You can also enable or disable cors for all or specific responses or set custom headers.

🚀 npmjs.com/package/aws-ts

Collapse
 
prakashr87 profile image
Prakash

Article was very nice, Helped me to start AWS Lambda.

    Actually iam new to Aws Lambda. I have Node.js REST API project(which running on EC2 now) with 100+ api's and 50+ routes and model files. How can i deploy my project into AWS Lambda?.  

Thanks.

Collapse
 
striderhnd profile image
Erick Gonzales

Nice article Adnan, I'm developing a service with Lambda but I'm having a little issue for some reason, my API Gateway is timing out, not sure if is the mongo connection or other issue related to Lambda

Collapse
 
jmtyra profile image
Jmtyra

Thanks for making this article! I love the stuff for beginners like myself. :) Not sure if anyone else ran into this issue but the 'sls' command wouldn't work and I had to use 'serverless' instead. Might be because I'm on a Windows box? Not sure. Either way this was a lot of fun, thanks again! :)

Collapse
 
eduardomarcolino profile image
Eduardo Marcolino

Great article. Thank you very much for posting it!
I'm definitely going to try this thing out.

Collapse
 
anirudhmaddy profile image
Anirudh

How do I test the application in the local environment before deploying it to AWS?

Some comments may only be visible to logged-in visitors. Sign in to view all comments.