Six reasons why you shouldn’t run Express.js inside AWS Lambda

Tradeoffs to consider when choosing to use Express in Lambda. Some thoughts on why using Express.js inside AWS Lambda is a design anti-pattern and how to give it up without pain.

Maxim Vynohradov
DailyJS
5 min readDec 7, 2020

--

Photo by Markus Spiske on Unsplash

Over the last few years the popularity of npm packages that allow you to use Express.js inside an AWS Lambda handler quickly blew up.

Instead of a plain AWS Lambda handler, these packages provide functionality, allowing you to run Express.js middlewares — controllers with some limitations.

Some examples of such libraries:

But why have developers decided to do this? Here are just a few of the reasons that I’ve seen in practice:

  • No interest to learn new approaches to writing handlers for API;
  • For different reasons — wanting to use serverless architecture, but have no time to adapt and re-write existing Express.js based solutions to Lambda handlers;
  • Wish to use the existing Express.js functionality and ecosystem, mostly it’s about huge numbers of third-party middleware;
  • Trying to reduce costs using AWS Lambda instead of a development server (like EC2, AWS ECS, AWS EKS, etc).

After checking discussions on Reddit about this article I understood that a better title/description would be (thanks for the advice):

Tradeoffs to consider when choosing to use Express in Lambda

The initial title becomes unexpectedly really controversial and defiant.

So below is a list of six reasons why the usage of Express.js inside AWS Lambda is in most cases redundant, you probably get many drawbacks from this approach.

1 — Increasing node_modules size and cold starts

A simple point: the bigger your node_modules, the bigger cold starts AWS Lambda will have. With no exceptions. Raw Express.js is near 541.1 KB, but you also need additional dependencies, mostly middleware, that can increase your node_modules size several times.

2 — Additional operational time

When you use standalone Express.js on the server (standard way), each HTTP request is some kind of text that the server parses to a well-known request object. Lambdas that people tried to use with Express.js inside, usually run under API Gateway or AWS Application Load Balancer, and data that comes from this event source is already parsed by API GW and ALB! (Yes, it’s different, but anyway).

When you use Express.js inside AWS Lambda your “system” makes the next thing with input HTTP data:

  1. AWS API GW or AWS ALB parses HTTP request and convert them to the event payload.
  2. The library that wraps the Express.js server maps the lambda event to the server request.
  3. Express.js one more time converts this to its request object.
  4. It the similar to a response — the library that wraps Express.js converts HTTP response to AWS Lambda response object.

So many supplementary conversions. Sometimes it looks like just wasting processor time.

3 — AWS Lambda has a different limitation, that can be unexpected for your Express.js application:

  • First of all, lambdas are stateless — each AWS Lambda instance is an AWS Firecracker container, that will shut down after some period of inactivity. So you cannot simply persist data and share them across all lambda instances. The same situation is true with sessions — to use it with AWS Lambda, you need additional storage, for instance, Redis instance hosted as AWS ElasticCache.
  • Lambdas container can live during several handler executions (warm lambdas), but in any way, it quits unexpectedly. And this could break some tools or make their behavior unpredictable. The most impressive case is related to buffering, loggers, and any error trackers, like Sentry. Usually, they don’t send all logs, and data immediately, they buffering them first, and then send several logs items at once, to make this more efficient. But when your lambda’s container quits, time-to-time these buffers do not have time to be flushed into storage or third-party services. For sure, we can disable buffering, but some of the services require other SDKs, that are specific for AWS Lambda. And they cannot be re-used simply as Express.js middleware — you should wrap them up as your middleware, that double work.
  • Also, you cannot use web-sockets (WebSockets, socket.io) inside the Express.js application, for the same reason — the lifetime of lambda execution container. But at the same time, AWS API GW supports web sockets, but they are implemented in another way, you cannot connect socket.io to them.

4 — Some things that you are used to do in the Express.js app are different in AWS Lambda and has more adequate alternatives

Despite all disadvantages, the embedded middleware pattern in Express.js is probably one of the most popular things in the Node.js world. However, there is no need to use Express.js just for this, becasue at least one middleware library is better suited for AWS Lambda:

Also, it implements an onion-like middleware pattern, that is much more flexible than Express.js can provide for you.

5 — Best practices for Express.js and AWS Lambda are different

At least you can easily find out the next point — security protection approaches are different. When Express.js best practice guide proposes to use Helmet.js library, it doesn’t apply to AWS Lambdas. AWS proposes to use the AWS WAF service that:

Protects your web applications from common web exploits

6 — Lost benefits from individual packaging of lambdas

When you write classic AWS Lambda handlers, you usually can package each lambda artifact separately, to reduce each artifact size. But when you use Express.js, you cannot do this — all lambdas require the same dependencies. Technically you can, but all of them will have the same size, which negates their advantages. Also, in this case, serverless-webpack-plugin cannot optimize imports correctly, because technically each lambda will have the same dependencies tree.

Despite all of the above, I believe, that there are some cases when the usage of Express.js inside AWS Lambda is valid and justified:

  • Pet projects — Because of the great AWS Free Tier, you can probably run them for free.
  • If your service is not mission-critical, and you are okay with all issues described above — so, okay, you can use it without any doubts (but don’t forget about technical debt).

I hope this information will be somewhat useful and you will not forget these points the next time you decide to use Express.js inside AWS Lambda.

--

--

Maxim Vynohradov
DailyJS

Senior Software Engineer (Node.js, AWS, Serverless, React.js)