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:
Create an AWS Lambda function:
We then drop in our Node.js script:
After which we'll then associate our AWS Lambda function with an AWS API Gateway:
... like so:
Testing Methodology
We want to test server-setups that are:
- Functionally identical - they implement the same API's and services
- Without a DB - we just want to see what kinds of performance the servers alone provide
- The servers should be hosted on Amazon AWS so that latency is fairly included in testing metrics
- The servers should mimic a production server with full API, routes, and view handling
- Since AWS Lambda autoscales - we'll supply our Node and Express servers with 8 workers serverside to compensate
- 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!