Express.js and AWS Lambda — a serverless love story
If you are a Node.js developer or you’ve built an API with Node.js, there’s a big chance you used Express.js. Express is de facto the most popular Node.js framework.
Express apps are easy to build. For a simple app, you just need to add a few routes and route handlers. That’s it.
A simple, traditionally hosted Express.js app, with a single request.
For example, the simplest Express app looks like the following code snippet:
'use strict'
const express = require('express')const app = express()
app.get('/', (req, res) => res.send('Hello world!'))
const port = process.env.PORT || 3000app.listen(port, () =>
console.log(`Server is listening on port ${port}.`
))
If you save that code snippet as app.js in a new folder, you are just three steps away from having a simple Express app:
- Create a new Node.js project. To do so, run the
npm init -y
command in your terminal. Just make sure you navigated to the folder that containsapp.js
first. - Install the Express module from NPM by running the
npm install express --save
command from terminal. - Run the
node app.js
command, and you should see “Server is listening on port 3000.” as a response.
Voila! You have an Express app. Visit http://localhost:3000 in your browser, and you’ll see a “Hello world!” message.
Application deployment
Now comes the hard part: How can you show it to your friends or family? How to make it available for everyone?
Deployment can be long and painful process, but let’s imagine you manage to do it quickly and successfully. Your app is available to everyone and it lived happily ever after.
Until one day, an unexpected an army of users started using it.
Your server struggled, but it worked.
A simple, traditionally hosted Express.js app under load.
At least for some time. And then it died. ☠️
A simple, but dead, traditionally hosted Express.js, that crashed because too many users accessed it.
An army of users is angry (at least they didn’t pay for the app — or did they?) You are desperate and trying to Google the solution. Can the cloud help?
Cloud should fix your scaling issues, right?
And you’ve met one of your annoying friends again. She’s talking about that serverless thingy again. But come on, you still have a server. It just belongs to somebody else and you have no control over it.
But, there are servers!
But you are desperate, you would try anything, including black magic and even serverless. “What the heck is that serverless thingy, anyway?”
You ended up with many links, including the one to the free first chapter of “Serverless Apps with Node and Claudia.js” by Manning Publications.
That chapter explains serverless with washing machines!? Sounds crazy, but it kinda makes sense. 💩 already hit the fan, so you decide to try it.
Making your Express.js app serverless
That chapter was all about serverless on AWS. And now you know that Serverless API consists of an API Gateway and AWS Lambda functions. But how can you go serverless with your Express app?
This sounds as promising as that movie about Matt Damon shrinking…
How do you fit your Express.js app into AWS Lambda?
Claudia could help you to deploy your app to AWS Lambda — lets ask her for help!
Make sure you configured your AWS access credentials as explained in this tutorial before running Claudia commands.
Your code should be slightly modified to suppor AWS Lambda and deployment via Claudia. You need to export your app
instead of starting the server using app.listen
. Your app.js
should look like the following code listing:
'use strict'
const express = require('express')const app = express()
app.get('/', (req, res) => res.send('Hello world!'))
module.exports = app
That would break a local Express server, but you can add app.local.js
file with the following content:
'use strict'
const app = require('./app')
const port = process.env.PORT || 3000app.listen(port, () =>
console.log(`Server is listening on port ${port}.`
))
And then run the local server using the following command:
node app.local.js
To make your app work correctly with AWS Lambda, you need to generate AWS Lambda wrapper for your Express app. With Claudia, you can do so by running the following command in your terminal:
claudia generate-serverless-express-proxy --express-module app
where app
is a name of an entry file of your Express app, just without the .js
extension.
This step generated a file named lambda.js
, with the following content:
'use strict'
const awsServerlessExpress = require('aws-serverless-express')
const app = require('./app')
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)
That’s it! Now you only need to deploy your Express app (with lambda.js
file) to AWS Lambda and API Gateway using the claudia create
command.
claudia create --handler lambda.handler --deploy-proxy-api --region eu-central-1
After a few moments, the command finished and printed the following response:
{
"lambda": {
"role": "awesome-serverless-expressjs-app-executor",
"name": "awesome-serverless-expressjs-app",
"region": "eu-central-1"
},
"api": {
"id": "iltfb5bke3",
"url": "https://iltfb5bke3.execute-api.eu-central-1.amazonaws.com/latest"
}
}
And if you visit the link from that response in your browser, it prints “Hello world!” It worked! 🙀
Serverless Express app.
With a serverless app, your army of users can continue growing and your app will still be working.
It is possible, because AWS Lambda will auto scale up to 1000 concurrent executions by default. New functions are ready a few moments after the API Gateway receives the request.
Serverless Express.js app under heavy load.
But this is not your only benefit. You also saved money besides having a stable app under a higher load. With AWS Lambda, you pay only for requests you used. Also, the first million requests each month are free, as part of a free tier.
Your serverless app also saves your money!
To read more about the ways your business benefits through serverless, see this article.
Limitations of serverless Express.js apps
Serverless Express apps sound awesome, but they have some limitations.
Serverless, the limited edition.
Some of the important limitations of serverless Express apps are the following:
- Websockets don’t work with AWS Lambda. That’s because your server doesn’t exist when there are no requests. Some limited support for websockets is available through AWS IOT websockets over MQTT protocol.
- Upload to the file system will not work either, unless you are uploading to the
/tmp
folder. That’s because the AWS Lambda function is read-only. Even if you upload files to/tmp
folder, they will exist for a short time, while the function is still “warm”. To make sure your upload feature is working fine, you should upload files to AWS S3. - Execution limits can also affect your serverless Express app. Because API Gateway has a timeout of 30 seconds, and AWS Lambda’s maximum execution time is 5 minutes.
This is just a beginning of a serverless love story between your apps and AWS Lambda. Expect more stories soon! Follow me at https://twitter.com/slobodan_ and https://effortless-serverless.com for more.
As always, many thanks to my friends Aleksandar Simović and Milovan Jovičić for help and feeback on the article.
All illustrations are created using SimpleDiagrams4 app.
If you want to learn more about serverless Express and serverless apps in general, check out “Serverless Apps with Node and Claudia.js”, the book I wrote with Aleksandar Simovic for Manning Publications:
Serverless Apps with Node and Claudia.js
You can also use claudia40
promo code for 40% discount on Mannings website.
The book will teach you more about serverless Express apps, but you’ll also learn how to build and debug a real world serverless API (with DB and authentication) using Node and Claudia.js. And how to build chatbots, for Facebook Messenger and SMS (using Twilio), and Alexa skills.