Simple steps to deploy an Express server to AWS Lambda with Claudia.js

John Dyer
8 min readAug 18, 2019

--

Express is one of the most popular and flexible JavaScript frameworks for building a server. However, managing your own server involves a lot of work, and people are increasingly building serverless applications, using services such as AWS Lambda and API Gateway that abstract away the underlying infrastructure.

In many cases, serverless applications offer benefits over traditional applications. In addition to not having to manage servers, you only pay for the computing power you use, not the time your application’s deployed. However, learning all the intricacies of any particular platform, such as AWS Lambda, can require a great deal of time and effort. Also, from a development perspective, if you structure your code to be run as standalone Lambda functions, you’ll likely find yourself writing duplicate code as you need similar functionality across functions.

Fortunately, there are tools like Claudia, which allow you to build a regular Express server, and convert it to an app that can be deployed to AWS Lambda, with minimal configuration and effort. In this tutorial, I will demonstrate how to build an Express server that you can run locally, and how to deploy it to AWS Lambda. The final result is also on GitHub.

This article assumes you have some basic familiarity with Express. If you’ve never used it before, I’d recommend you read their getting started guide first.

Setting up a simple server

Let’s create our server. We’ll start by creating a new Node.js application. Create a directory called “express-lambda-example”, change into it, and run the command npm init. You can accept all the default options.

Once we’ve done that, we can install Express:

npm install --save express

Now let’s build a simple server with Express. For this example, I’ve created some dummy JSON data of “posts” and “users”. Our server will contain endpoints to get these post and user objects.

First, create a directory “data”, and add two files, “posts.json” and “users.json”:

[
{ "id": "post1", "author": "user1", "title": "Create an Express server" },
{ "id": "post2", "author": "user2", "title": "Learn AWS Lambda" },
{ "id": "post3", "author": "user3", "title": "5 reasons to go serverless" },
{ "id": "post4", "author": "user2", "title": "A guide to claudia.js" },
{ "id": "post5", "author": "user2", "title": "Getting started with AWS API Gateway" },
{ "id": "post6", "author": "user3", "title": "Writing a useful tutorial" }
]
[
{ "id": "user1", "firstName": "John", "lastName": "Doe", "location": "Boston" },
{ "id": "user2", "firstName": "Jane", "lastName": "Doe", "location": "San Fransisco" },
{ "id": "user3", "firstName": "Joe", "lastName": "Smith", "location": "New York" }
]

Now create a directory “routes”, with two files, “postRouter.js” and “userRouter.js”. These files will define the endpoints to get our post and user data.

const { Router } = require('express');
const posts = require('../data/posts.json');
const postRouter = Router();// endpoint to get all posts
postRouter.get('/posts', (req, res) => {
res.json(posts);
});
// endpoint to get a specific post
postRouter.get('/posts/:postId', (req, res) => {
const { postId } = req.params;
const post = posts.find(post => post.id === postId);
if (post) {
res.json(post);
} else {
res.status(404).send('Post not found');
}
});
module.exports = postRouter;
const { Router } = require('express');
const users = require('../data/users.json');
const userRouter = Router();// endpoint to get all users
userRouter.get('/users', (req, res) => {
res.json(users);
});
// endpoint to get a specific user
userRouter.get('/users/:userId', (req, res) => {
const { userId } = req.params;
const user = users.find(user => user.id === userId);
if (user) {
res.json(user);
} else {
res.status(404).send('User not found');
}
});
module.exports = userRouter;

Finally, create a file “server.js”. This file will setup our express server, using the routes we just defined, and start it.

const express = require('express');
const postRouter = require('./routes/postRouter');
const userRouter = require('./routes/userRouter');
// create the server and setup routes
const app = express();
app.use(postRouter);
app.use(userRouter);
app.get('/', (req, res) => res.send('Express Lambda Example Server'));
// run the server locally
app.listen(3000, () => console.log('Server listening at http://localhost:3000'));

Now we can run our server locally. Run node server.js . You should see Server listening at http://localhost:3000 printed in the console. Open your web browser, go to that link, and you should be able to access the endpoints we defined:

Adding Claudia

Now that we have our server, let’s add Claudia and configure it so we can deploy to AWS lambda.

npm install --save-dev claudia

We have to make one small change to our application before we can use Claudia. It requires that we export our Express server from some file. In “server.js”, replace the last line that starts the server locally with an export statement:

module.exports = app;

Create a new file “local.js” that imports the app and runs it using the line we just replaced:

const app = require('./server');// run the server locally
app.listen(3000, () => console.log('Server listening at http://localhost:3000'));

From now on, we can run our app locally with node local.js.

With this change made, we can add a script to configure Claudia. In “package.json”, within the “scripts” section, add the following script:

"init-claudia": "claudia generate-serverless-express-proxy --express-module server"

By specifying --express-module server , we’re telling Claudia our Express server is exported from “server.js”. Further documentation on this command is available here. Let’s go ahead and run it:

npm run init-claudia

When it finishes, you’ll see we have a new file “lambda.js”, that contains our lambda function:

'use strict'
const awsServerlessExpress = require('aws-serverless-express')
const app = require('./server')
const binaryMimeTypes = [
'application/octet-stream',
'font/eot',
'font/opentype',
'font/otf',
'image/jpeg',
'image/png',
'image/svg+xml'
]
const server = awsServerlessExpress.createServer(app, null, binaryMimeTypes);
exports.handler = (event, context) => awsServerlessExpress.proxy(server, event, context)

This code uses the aws-serverless-express module to map incoming requests from Amazon API Gateway to Express routes in our Lambda function. This saves us from having to manually set up routes in API Gateway or functions in Lambda.

Configuring AWS Credentials

The last step before we deploy our server to AWS is to create an AWS account, and configure credentials on our system so that Claudia will be able to use it.

You can sign up for an AWS account here. Once that’s done, there are a couple different ways to configure our credentials, but the process I’m going to use involves three steps: generating AWS credentials through the IAM console, installing the AWS CLI, and using the CLI to set my credentials.

Generating AWS Credentials

First, go to the AWS IAM console. We’re going to create a new IAM user with all the permissions Claudia requires. Select “Users”, and on that page, “Add User”:

You can enter whatever you want as the user name, but be sure to check the “Programmatic access” option:

Claudia requires three policies: AmazonAPIGatewayAdministrator, AWSLambdaFullAccess, and IAMFullAccess. Select “Attach existing policies directly”, search for the three policies, and check each:

We can skip step 3. When you reach the review step, be sure AWS access type is “Programmatic access” and that “IAMFullAccess”, “AWSLambdaFullAccess”, and “AmazonAPIGatewayAdministrator” appear under permissions:

Go ahead and create the user. On the final screen, be sure to copy down both the “Access key ID” and “Secret access key”, or download the CSV containing them. We will need them after installing the AWS CLI.

Installing the AWS CLI

To install the AWS CLI, I’m going to use pip, the package manager for Python, as I already have Python installed on my system. If you don’t have Python, you can download it here. AWS also describes some alternative installation methods here.

Use the following command to install the CLI with pip:

pip install awscli --upgrade --user

To verify the CLI’s installed properly, run aws --version . You should something like the following:

aws-cli/1.16.220 Python/3.7.4 Darwin/17.7.0 botocore/1.12.210

If you run into any errors installing the CLI, especially if you’re using a Mac like I am, you might want to try installing the CLI in a Python virtual environment.

Configuring the AWS CLI

With the AWS CLI installed, we just need to configure it with the credentials we generated. To do this, run aws configure , and it’ll prompt you for some information.

For “AWS Access Key ID” and “AWS Secret Access Key”, enter the credentials you copied down. For “Default region name”, you can use the default of “us-east-2”, or provide another region from this list of regions that support API Gateway and Lambda. For “Default output format”, simply accept the default.

Deploying to AWS

We’re finally ready to use Claudia to deploy our server to AWS API Gateway and Lambda. Add two more scripts to “package.json”:

"create-api": "claudia create --handler lambda.handler --deploy-proxy-api --region us-east-2",
"update-api": "claudia --update"

The “create-api” script will deploy our server to AWS for the first time. By specifying —-handler lambda.handler, we’re telling Claudia to deploy the “handler” function in “lambda.js”. Once it’s deployed for the first time, we can use the “update-api” script to deploy any updates. More information on Claudia’s various commands and options is available here. Let’s go ahead and run the “create-api” script:

npm run create-api

The first time I ran this, I encountered the error “The AWS Access Key Id needs a subscription for the service”. Turns out I simply forgot to verify my new AWS account.

Once the deploy is complete, it’ll save some configuration to “claudia.json”:

{
"lambda": {
"role": "express-lambda-example-executor",
"name": "express-lambda-example",
"region": "us-east-2"
},
"api": {
"id": "8z6d9k59gb"
}
}

And that’s it, our server is live! Go to https://[API ID].execute-api.[AWS REGION].amazonaws.com/latest to check it out:

Wrapping up

You now know how to deploy an Express server to AWS, without even having to touch Lambda or API Gateway. I’d still recommend you check out Lambda and API Gateway out in the console, just to see what Claudia actually did.

One thing I’ll point out is that instead of creating multiple API Gateway endpoints, each with their own Lambda function, Claudia created a single Lambda function with our same app structure that handles routing internally. No matter how complicated our Express application is, the approach we took to deploying would work.

If you’re interested to learn more about Claudia, you can check out some of their tutorials and examples. All the code used in this tutorial is on GitHub for reference. If you found this tutorial useful, feel free to give it a clap, and until next time, best of luck coding!

--

--

John Dyer

I’m a software engineer in the Boston area, specializing in web development. I also build mobile and desktop apps using React Native and Electron.