Deploying an Express Application to AWS Lambda, the easy way

In this post I’ll take an existing Express app I wrote for a previous blog post and show you how to deploy it to AWS. I’ll be using two libraries: serverless and aws-serverless-express.

** Note: you can find the complete code here.

Let’s start by adding some libraries to our app:

yarn add serverless serverless-plugin-write-env-vars --dev
yarn add aws-serverless-express

And we’ll write a .env.yml file to hold our sensitive data. (make sure to add .env.yml to your .gitignore)

# .env.yml
secretAccessKey: YOUR_AWS_SECRET_KEY
NODE_ENV: development
DATABASE_URL: postgres://todo_user:todo_password@localhost/todo
NODE_ENV: production
DATABASE_URL: postgres://
NODE_ENV: production
DATABASE_URL: postgres://

You’ll need to replace the values for the following keys:

  • accessKeyId and secretAccessKey: you can follow this instructions if you don’t have AWS credentials yet.
  • DATABASE_URL: you can provision free Postgres databases on Heroku, and you can get the url value from there (just make sure to add ?ssl=true to the end of the url)

Now we’ll use the aws-serverless-express library to create a lambda function that will interact with our express application.

# lambda.js
'use strict';
const awsServerlessExpress = require('aws-serverless-express');
const app = require('./app');
const server = awsServerlessExpress.createServer(app);
exports.handler = (event, context) => awsServerlessExpress.proxy(server, event, context);

The last piece of the puzzle is creating the serverless.yml file, which lets serverless know how to deploy our app.

# serverless.yml
service: todoapi
name: aws
runtime: nodejs4.3
credentials: ${file(./.env.yml):credentials}
NODE_ENV: ${file(./.env.yml):${opt:stage}.NODE_ENV}
DATABASE_URL: ${file(./.env.yml):${opt:stage}.DATABASE_URL}
- serverless-plugin-write-env-vars
handler: lambda.handler
- http: ANY {proxy+}

The serverless.yml file may seem a little complex, but let’s break it down piece by piece.

  • service: our app name
  • provider: 
    - name: serverless only supports aws at the moment
    - runtime: when writing your app, make sure you’re using a node version supported by AWS Lambda (at the time of this writing 4.3 was recommended)
    - credentials: here we’re reading the credentials variable from the .env.yml we wrote earlier. You can read more on how serverless deals with variables here.
  • custom:
    - writeEnvVars: during deployment,serverless-plugin-write-env-vars will grab any value defined inside custom.writeEnvVars and create a .env file for us, which our app can then read.
    We’re grabbing values from our .env.yml file, like we did for credentials. However, notice we’re also using an opt:stage variable, which will be populated from the CLI --stage option when we run the serverless deploy command.
  • plugins: include any serverless plugin here.
  • functions: a regular serverless app would usually have several functions defined. However, we’ll only use a single function that will cede control to our express app.
    - api: I’m calling my function api, but you can use any name you prefer. This name will be used to identify your function when running serverless commands like serverless logs.
    - handler: here we specify we want to execute the handler function we previously wrote on our lambda.js file.
    - events: a lambda function can be triggered from different events (an S3 bucket upload, an SNS topic, HTTP requests, etc). For this example, we’ll only deal with HTTP requests.
    - http: here we’re saying we want to accept any http method to any path, and just hand it over to our express app.

Now we’re ready to deploy our app. Serverless allows you to deploy to different environments or “stages”. Let’s deploy to staging first:

serverless deploy --stage staging

After serverless is done uploading your app, you’ll get your app URL, which should look something like:

Take some time to test your app. You can check out app logs with:

serverless logs --stage staging --function api -t

And once you’re happy, deploy to production with

serverless deploy --stage production

That’s it. Your app is now running on AWS Lambda!

Special Considerations:
Do not rely on memory for storing data (if you’re using in-memory sessions, you’ll have to move them to a database, or go stateless).

You can take a look at the complete source code for this blog post here.

Show your support

Clapping shows how much you appreciated Alexis Hevia’s story.