Express.js app serverless, using Lambda or Functions - a good idea?

The answer to your question depends upon your current and future needs. I think you should always plan ahead and make sure that the current infrastructure that you will implement can be upgradable for future needs.

You should ask yourself those questions:

  • In the future do I want to have some websocket connection?
  • Do I need any proxy in my request routing?
  • How big will become my application over time?
  • Which AWS Service will I expect to use in the future

Scalability

Using Express.js in a lambda is not a good idea for many reasons:

  1. You will pay more for the execution of your Lambda because it will take more time to run and maybe more memory
  2. Latency is higher
  3. Doing a small modification means redeploying all your application code on 1 lambda so only 1 point of failure.
  4. Normally overtime your application's code base will grow as you add more features. The maintenance of that monolithic repo will be a pain in the ass and you will deploy less than you want because of the bugs you might encounter.

Cost effectiveness

Express.js on Lambda is more expensive because you need to proxy any method into your lambda using a API Gateway REST API rather then using API Gateway HTTP API

HTTP APIs are up to 71% cheaper compared to REST APIs

Latency

Lambda isn't magically executing your code without server even tho they market it like it is. When an event occur, AWS will launch a docker container, wait for it to fully load all your dependencies and then run your handler.

With a normal Node.js server on AWS EC2 or AWS ECS it's a one time cost because your server is always running and all your dependencies are already loaded but not on your lambda.

As AWS says:

This approach [Express.js and proxy all requests to your lambda] is generally unnecessary, and it’s often better to take advantage of the native routing functionality available in API Gateway. In many cases, there is no need for the web framework in the Lambda function, which increases the size of the deployment package. API Gateway is also capable of validating parameters, reducing the need for checking parameters with custom code. It can also provide protection against unauthorized access, and a range of other features more suited to be handled at the service level.

Best practices for organizing larger serverless applications

How to convert Express framework into plan Lambda

To simplify your life, I would suggest you to use SAM CLI. It's very simple to get started with it.

Install SAM CLI

If you're following the MVC pattern for your Express app. You only need to take your service files where your core logic live.

The folder structure I like to use for the lambda is the following

Suppose it's a small calendar app

──src-ts
    ├───handlers
    │       getEvent.ts
    │
    ├───tests
    │   │   getEvent.tests.ts
    │   │
    │   └───utils
    │           utils.ts
    │
    └───utils
            utils.ts
            validation.ts

It's important that your handler returns 3 thing

  1. Headers (JSON)
  2. statusCode (number)
  3. Body (stringified)

You also need a template.yml file to describe the infrastructure that your lambda need

AWSTemplateFormatVersion: 2010-09-09
Description: Describe the lambda goal

Transform:
    - AWS::Serverless-2016-10-31


Resources:
    # API Gateway
    LambdaAPI:
        Type: AWS::Serverless::Api
        Properties:
            StageName: StageName
            Cors:
                AllowMethods: "'POST, GET, OPTIONS'"
                AllowHeaders: "'*'"
                AllowOrigin: "'*'"


    # IAM Role
    LambdaRole:
        Type: AWS::IAM::Role
        Properties:
            AssumeRolePolicyDocument:
                Version: 2012-10-17
                Statement:
                    - Action:
                          - 'sts:AssumeRole'
                      Effect: Allow
                      Principal:
                          Service:
                              - lambda.amazonaws.com
            ManagedPolicyArns:
                - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
                - arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess
                - arn:aws:iam::aws:policy/AmazonAPIGatewayInvokeFullAccess

    GetEvent:
        Type: AWS::Serverless::Function
        Properties:
            Runtime: nodejs12.x
            Timeout: 180
            FunctionName: youLambdaName
            Handler: src/handlers/getEvent.handler
            Role: !GetAtt LambdaRole.Arn
            Events:
                Get:
                    Type: Api
                    Properties:
                        RestApiId: !Ref LambdaAPI
                        Path: /events/{eventid}
                        Method: GET

Note I used typescript and but when compiled it's creating an src folder

Some resource to help you more in depth:

  • https://aws.amazon.com/blogs/compute/going-serverless-migrating-an-express-application-to-amazon-api-gateway-and-aws-lambda/
  • https://dev.to/brightdevs/how-to-convert-an-express-app-to-aws-lambda--44gc
  • https://medium.com/hackernoon/how-to-deploy-a-node-js-application-to-aws-lambda-using-serverless-ae7e7ebe0996
  • https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html

To Conclude

The upside of using lambdas without Express are:

  • better scalability
  • cost optimization
  • lower latency
  • higher availability because you have multiple lambda for each business logic instead of one that run it all

The downside of using lambdas without Express are:

  • You need to modify your existing code
  • Init time of your lambda needs to be part of your thinking when developing your logic
  • You need to learn SAM yaml template and read the AWS doc when you wanna add more functionality to your API infrastructure.

Take advantage of the AWS infrastructure, don't try to go against it. All AWS Services are working together in a seamless and low latency way. You should remove Express from your infrastructure if you wanna go "Serverless".


If it's a bit hefty to be function as you said, you may think in creating a docker image for your app and run it using Fargate ECS which is considered a serverless option.


Dividing your server into small microservices hosted on lambda scripts is going to help you big time.

I have broken my server into few microservices to make a scalable system. Below are the mentioned lambda functions you can typically create in order to make it cost efficient and scalable

  1. User Data Management
  2. Authentication
  3. User specific web interfaces/APIs
  4. core logic
  5. Third party integrations

etc...

  • Scalability: You won't get automated scalability without manual configuration or without using any third party softwares which can adjust the scalability on EC2 or any other servers

  • Cost Effectiveness: As lambda functions have high availability and billing per API call, you can actually get more out from your budget.

  • Security: Breaking down each web app in different lambda function will give you more flexibility in providing access for different types of users as well as API gateway configuration for each individual web app and load balancers will be a great security layer in addition to the authentication system.

It doesn't matter if you're creating your app from scratch or not. All the functions included in the server will be divided into small chunks of code and there's a lot of flexibility to test and run each application one by one as you build them.

With Express, I haven't seen this kind of flexibility and rapid development.