AWS Lambda and Express – Getting Started Guide

What are we building?

In this guide, we will learn how to set up a simple Node.js Express API, powered by an AWS Lambda function, and what are the pros and cons for such a setup. I hope that it can help developers get the fastest setup as possible with Express/AWS Lambda and make their journey better using serverless, less frustrating and enjoyable.

Let’s jump right in!

The repository for this guide can be found at GitHub.

Prerequisites

Make sure Node.js is installed and the version is Node.js 8 or above. Download and install Node.js, or follow this guide.

Installing Serverless Framework

Next, install the Serverless Framework via npm which was already installed when you installed Node.js.

npm install -g serverless

Setting up an AWS Account with Serverless Framework

If you haven’t signed up to AWS yet, you can sign up at AWS Free Tier Account.

Serverless is the most widely-adopted toolkit for building serverless applications. The Serverless Framework is an open-source CLI for building and deploying serverless applications.

It currently supports major serverless providers such as AWS, Google Cloud, Azure, fn, and more.

After installation, configure serverless with the AWS account key and secret:

serverless config credentials --provider aws --key xxxxxxxxxxxxxx --secret xxxxxxxxxxxxxx

Where the key and secret are the account key and account secret provided by AWS.

Install dependencies

Let’s install `express`, `cors` and `serverless-http`. You can add any dependencies your app requires. CORS is a mechanism that allows restricted resources on a web page to be requested from another domain outside the domain from which the first resource was served.

The cors npm package is a node.js package for providing a Connect/Express middleware that can be used to enable CORS with various options.

We’ll install ‘serverless-http’ npm package. This is required to convert AWS Lambda event to an express request/response object. ‘serverless-http’ cleans up the event, sanitizes headers, checks if the header is base64 encoded and dispatches an async callback. ‘serverless-http’ currently supports Express, Koa, connect, Hapi.js and more.

npm install --save express cors serverless-http

From serverless-http README on GitHub:

Your code is running in a serverless environment. You cannot rely on your server being ‘up’ in the sense that you can/should not use in-memory sessions, web sockets, etc. You are also subject to provider-specific restrictions on request/response size, duration, etc.”

Next, we will setup our serverless.yml config.

serverless.yml

This is the configuration file for the function we need. Notice that we use two events for the function: Root route / and a proxy {proxy+} to make express handle the rest of the requests.

You can read more about serverless.yml in the documentation.

service: expressAPI-epsagon

provider:
 name: aws
 runtime: nodejs8.10
 stage: dev
 region: eu-central-1
functions:
 server:
   handler: index.server
   events:
     - http: ANY /
     - http: 'ANY {proxy+}'

index.js

This is our Lambda entry point. Notice that the server can be any supported web server from `serverless-http`, making the function pluggable to Express, koa, connect, hapi.js (experimental) and more. Serverless wraps our app and returns a promise, that way we can use async-await instead of the callback-based Lambda functions.

const serverless = require('serverless-http');
const server = require('./app');
const handler = serverless(server);
module.exports.server = async (event, context) => {
 return await handler(event, context);
};

Express Server (app.js)

This is a simple express API example.

const express = require('express');
const cors = require('cors');
const apiRouter = require('./api');
const errorHandler = require('./helpers/errorHandler');
const server = express();
server.use(cors());
server.use(express.urlencoded({ extended: true, strict: false }));
server.use(express.json());
server.get('/', (req, res) => {
 res.json({ message: 'Express API Powered by AWS Lambda!' });
});

server.use('/v1/api', apiRouter);
server.use(errorHandler.notFound);
server.use(errorHandler.internalServerError);
module.exports = server;

Deployment

Time to make our server come to life! Hit this command, sit back a minute, and let serverless magically deploy our function to AWS.

serverless deploy

It’s live. See endpoints for the full link. (it will generate different URL for different functions).

Notice the region and stage of the function – in our case, it’s eu-central-1 and in dev mode. It’s configurable in serverless.yml.

Root route: `\`

API route: `\v1\api`:

Troubleshoot

In case you run into any errors, you need to display logs:

serverless logs -f server -t

-f is the function name, -t is the function tail (latest request log). In case of error, it will display the error message and stack. This request is successful:

Notice the RequestId, HTTP method and path and the Lambda invocation details: Init duration, Duration, Billed Duration, Memory Size, and Max Memory.

Observability

As your app grows, you will need a more precise picture of your app architecture, distributed tracing, cost tracking, function monitoring and visual debugging.

Let’s connect Epsagon to gain observability for your application.

Epsagon lambda wrapper

  1. Install epsagon-node:
npm install --save-dev epsagon-node
  1. Create an Epsagon account.
  1. Wrap main server function with epsagon in index.js:
const serverless = require('serverless-http');
const epsagon = require('epsagon');
const server = require('./app');
epsagon.init({
 token: ‘your-epsagon-key’, // Enter your epsagon token found in ‘settings’ view
 appName: 'express-lambda-blog',
 metadataOnly: false

});

const handler = serverless(server);
module.exports.server = epsagon.lambdaWrapper(
 async (event, context) => await handler(event, context)
);
  1. Deploy once again with serverless deploy.
  2. See your function as observed by Epsagon!

AWS Lambda Epsagon

Clean

Serverless has helper command to remove the function and all related resources (S3 bucket objects, CloudFormation Stack) completely. After you’re done playing don’t forget to remove resources to keep your AWS account clean: serverless remove.

Pros and Cons

Express API powered by AWS doesn’t require any server setup, it is scalable as you need it to be and speeds up development and deployment process so you can spend your time developing the business instead of servers and clusters.

This will provide your stack an ability to connect to other services AWS has to offer. E.g. Integration with AWS SNS, DynamoDb/RDS, S3, and much more.

But it doesn’t come without a cost.

Lambda functions have some limitations that you should be aware of:

  • Functions are stateless
  • Cold starts
  • Maximum execution duration per request
  • Function timeout (15min)
  • Invocation payload size: request and response (6MB synchronous, 256Kb asynchronous), and more.

Summary

In this guide, we’ve created a serverless Express application powered by AWS Lambda. We learned about the frameworks we need to get up and running as fast as possible. This example is simple but extremely flexible for getting started.

We will have a follow up on this guide, and add client-side application integration, with React and Angular guides stored on S3 with CloudFront.

Don’t miss some more of our serverless beginners: