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. We’ll examine what are the pros and cons of such a setup. With this in mind, I hope that it can help developers get the fastest setup as possible with Express / AWS Lambda and therefore, make their journey better, less frustrating and enjoyable using serverless.

The repository for this guide can be found at GitHub.

Prerequisites

First, 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

Second, 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

In case 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

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

Install dependencies

Now, 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.

Then, 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. Currently, ‘serverless-http’ 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 set up our serverless.yml config.

serverless.yml

Below, 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.

Also, 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 webserver 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

Now, 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 this 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 of 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

Given that your app grows, you’ll need a more precise picture of your app architecture, distributed tracing, cost tracking, function monitoring and visual debugging.

Now, let’s connect Epsagon to gain observability for your application.

Epsagon lambda wrapper

First, install epsagon-node:

npm install --save-dev epsagon-node

Then, create an Epsagon account.

Third, wrap the 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) );

Next, deploy once again with serverless deploy.

Finally, see your function as observed by Epsagon!

Clean

Additionally, Serverless has helper command to remove the function and all related resources (S3 bucket objects, CloudFormation Stack) completely. Nevertheless, 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. Because it’s scalable, it speeds up the development and deployment process. As a result, you’ll spend your time focusing on the business instead of servers and clusters.

In effect, 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.

Nonetheless, 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 conclusion, 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.

Also, 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.

Find out more about serverless in our blog: