Hey! Lately, I've been focused on frontend either demonstrating some cool ways to build your clients (Vue 2.5) or on comparing ways to improve your styling performance (CSS in JS).

This time, I'd like to dive into some backend-specific comparisons: Express is a fantastic way to simplify building your RESTful API's, Node provides an increasing amount of server power out of the box, and newcomer AWS Lambda Serverless Compute provides a convenient way to abstract some of your serverside architecture and reduce cost by paying-per-compute-cycle rather than by-instance or month-by-month.

To my knowledge, this will be one of the first articles to compare AWS Lambda in a headsup against Express and Pure Node. Let's see how the most recent versions compare to each other.

Node 8.10

We'll use Node 8.10 for our testing since it's the highest version useable by all three server implementations:

 "engines": {
    "node": "=8.10"
  }

Node has seen enormous improvements over the last few major releases. It's gotten faster, more stable, and much improved through richer and well-thought-out API's.

Express 4.16.3

We'll use Express 4.16.3 for our second test-scenario. We'll add the following dependency to our package.json:

    "express": "=4.16.3"

Express is the default go-to for most beginners and seasoned developers alike within the wider Node universe. Express simplifies and abstracts most route, API, server, and url operations with simple shortcuts. Express also provides clean middleware support for your apps.

Amazon AWS Lambda

AWS Lambda is a relative newcomer that promises to eventually disrupt backend architecture by providing a pay-by-compute-cycle business model. AWS Lambda abstracts most backend architecture allowing users to drop in simple scripts or code snippets that will be automatically executed upon an HTTP Request to an associated endpoint.

We'll be using AWS Lambda on the Apr 2, 2018 release which is also supported by the following sdk:

    "aws-sdk": "=2.226.1"

To get setup on AWS Lambda, log into Amazon AWS:

awslambda_1

Create an AWS Lambda function:

awslambda_2

We then drop in our Node.js script:

awslambda_3

After which we'll then associate our AWS Lambda function with an AWS API Gateway:

awsgateway_1

... like so:

awsgateway_6

Testing Methodology

We want to test server-setups that are:

  1. Functionally identical - they implement the same API's and services
  2. Without a DB - we just want to see what kinds of performance the servers alone provide
  3. The servers should be hosted on Amazon AWS so that latency is fairly included in testing metrics
  4. The servers should mimic a production server with full API, routes, and view handling
  5. Since AWS Lambda autoscales - we'll supply our Node and Express servers with 8 workers serverside to compensate
  6. We'll test each case with 1000 concurrent requests

More specifically, each server will be tested according to the following base cases:

    (1) Fetch One / Read One - REST GET
    (2) Fetch All / List - REST GET
    (3) Delete One - REST DELETE
    (4) Update - REST UPDATE
    (5) Create - REST POST 
    (6) Render View

Our aim will be to isolate our testing around the server API functionalities rather than data-layer and server interoperability since there are many variables that can modify our performance results.

Instead, we'll use a preset array of ten (10) objects data.json.js for all CRUD-related operations.

All tests will be performed on a 2016 Dell 15 XPS with:

    Intel Core i7-6700HQ Quad-Core (8 Logical Cores) at 2.60 GHz
    32 GB RAM
    Windows 10 Pro + Linux Subsystem and Cygwin
    NVIDIA 960M GPU
    256GB m2 PCIe SSD
    15.6" FHD Screen

Results

Server Operation First Run Second Run Third Run Avg
AWS GET ONE 369 ms 138 ms 329 ms 278.66 avg ms
Express GET ONE 382 ms 118 ms 272 257.33 avg ms
Pure Node GET ONE 360 ms 118 ms 290 ms 256 avg ms

Winner: Pure Node (256 avg ms)

Server Operation First Run Second Run Third Run Avg
AWS GET ALL 12916 ms 10574 ms 12511 ms 12,000 avg ms
Express GET ALL 141 ms 239 ms 274 218 avg ms
Pure Node GET ALL 102 ms 205 ms 199 ms 168.66 avg ms

Winner: Pure Node (168.66 avg ms)

Server Operation First Run Second Run Third Run Avg
AWS POST 228 ms 234 ms 367 ms 276.33 avg ms
Express POST 227 ms 214 ms 290 243.66 avg ms
Pure Node POST 201 ms 196 ms 304 ms 233.66 avg ms

Winner: Pure Node (233.66 avg ms)

Server Operation First Run Second Run Third Run Avg
AWS PUT 163 ms 278 ms 345 ms 262 avg ms
Express PUT 118 ms 272 ms 306 232 avg ms
Pure Node PUT 127 ms 292 ms 306 ms 241.66 avg ms

Winner: Express (232 avg ms)

Server Operation First Run Second Run Third Run Avg
AWS HTML 12774 ms 12230 ms 15170 ms 13391 avg ms
Express HTML 340 ms 82 ms 223 215 avg ms
Pure Node HTML 341 ms 79 ms 231 ms 217 avg ms

Winner: Express (215 avg ms)

Server Operation First Run Second Run Third Run Avg
AWS DELETE 383 ms 130 ms 351 ms 288 avg ms
Express DELETE 258 ms 114 ms 231 201 avg ms
Pure Node DELETE 283 ms 96 ms 325 ms 234.66 avg ms

Winner: Express (201 avg ms)

Takeaways

I thought that Pure Node would outright smash Express. I also thought AWS Lambda might present itself as an extremely dominant competitor. In both cases, I was wrong. As of 4/23/18, Express and Pure Node appear to be fairly closely matched. And both routinely beat AWS Lambda. Using 8-worker clusters probably helped out a lot here since our test-cases involved 1000 concurrent requests.

AWS Lambda is pretty weak at serving HTML but improves in performance the more often it's used (frequent use will leverage Amazon's built in caching).

AWS Lambda remains both highly cost-effective/cost-saving and performant but it largely depends on the specific function, frequency of use, and kind of operation being performed. Limited data operations (returning one item, deleting an item, etc.) are nearly as performant as Pure Node or Express without the month-by-month instance costs.

Check out GitHub for all the code used in this example!