In this short post, I would like to highlight the significant performance differences between two of the most widely used Node.js frameworks currently. We will analyze their performance through a simple NestJS project, examining how performance varies between the two under different use cases, and draw final conclusions about their pros and cons.
Simple GET request
As a first test, let’s examine a simple GET request that generates a random number from 1 to 100
ExpressJS
/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | (‾) |
/ __________ \ |__| \__\ \_____/ .io
execution: local
script: script.js
output: -
scenarios: (100.00%) 1 scenario, 50 max VUs, 40s max duration (incl. graceful stop):
* default: 50 looping VUs for 10s (gracefulStop: 30s)
running (10.0s), 00/50 VUs, 90840 complete and 0 interrupted iterations
default ✓ [======================================] 50 VUs 10s
✓ is status 200
checks.........................: 100.00% ✓ 90840 ✗ 0
data_received..................: 21 MB 2.1 MB/s
data_sent......................: 7.3 MB 726 kB/s
http_req_blocked...............: avg=1.66µs min=0s med=1µs max=2.64ms p(90)=1µs p(95)=1µs
http_req_connecting............: avg=514ns min=0s med=0s max=1.08ms p(90)=0s p(95)=0s
http_req_duration..............: avg=5.47ms min=695µs med=4.99ms max=209.89ms p(90)=6.55ms p(95)=7.37ms
{ expected_response:true }...: avg=5.47ms min=695µs med=4.99ms max=209.89ms p(90)=6.55ms p(95)=7.37ms
http_req_failed................: 0.00% ✓ 0 ✗ 90840
http_req_receiving.............: avg=11.29µs min=6µs med=10µs max=4.54ms p(90)=12µs p(95)=14µs
http_req_sending...............: avg=3.11µs min=1µs med=3µs max=1.13ms p(90)=3µs p(95)=4µs
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=5.46ms min=678µs med=4.97ms max=209.87ms p(90)=6.54ms p(95)=7.35ms
http_reqs......................: 90840 9078.688967/s
iteration_duration.............: avg=5.5ms min=720.87µs med=5.01ms max=211.59ms p(90)=6.58ms p(95)=7.4ms
iterations.....................: 90840 9078.688967/s
vus............................: 50 min=50 max=50
vus_max........................: 50 min=50 max=50
Fastify
/\ |‾‾| /‾‾/ /‾‾/
/\ / \ | |/ / / /
/ \/ \ | ( / ‾‾\
/ \ | |\ \ | (‾) |
/ __________ \ |__| \__\ \_____/ .io
execution: local
script: script.js
output: -
scenarios: (100.00%) 1 scenario, 50 max VUs, 40s max duration (incl. graceful stop):
* default: 50 looping VUs for 10s (gracefulStop: 30s)
scenarios: (100.00%) 1 scenario, 50 max VUs, 40s max duration (incl. graceful stop):
* default: 50 looping VUs for 10s (gracefulStop: 30s)
running (10.0s), 00/50 VUs, 231809 complete and 0 interrupted iterations
default ✓ [======================================] 50 VUs 10s
✓ is status 200
checks.........................: 100.00% ✓ 231809 ✗ 0
data_received..................: 40 MB 4.0 MB/s
data_sent......................: 19 MB 1.9 MB/s
http_req_blocked...............: avg=1.66µs min=0s med=1µs max=4.88ms p(90)=2µs p(95)=2µs
http_req_connecting............: avg=189ns min=0s med=0s max=1.29ms p(90)=0s p(95)=0s
http_req_duration..............: avg=2.1ms min=86µs med=1.97ms max=89.75ms p(90)=2.56ms p(95)=3.51ms
{ expected_response:true }...: avg=2.1ms min=86µs med=1.97ms max=89.75ms p(90)=2.56ms p(95)=3.51ms
http_req_failed................: 0.00% ✓ 0 ✗ 231809
http_req_receiving.............: avg=17.67µs min=5µs med=11µs max=9.13ms p(90)=30µs p(95)=43µs
http_req_sending...............: avg=6.51µs min=1µs med=4µs max=8.02ms p(90)=8µs p(95)=17µs
http_req_tls_handshaking.......: avg=0s min=0s med=0s max=0s p(90)=0s p(95)=0s
http_req_waiting...............: avg=2.08ms min=60µs med=1.95ms max=89.72ms p(90)=2.53ms p(95)=3.46ms
http_reqs......................: 231809 23176.774534/s
iteration_duration.............: avg=2.15ms min=144.79µs med=2.01ms max=91.3ms p(90)=2.6ms p(95)=3.58ms
iterations.....................: 231809 23176.774534/s
vus............................: 50 min=50 max=50
vus_max........................: 50 min=50 max=50
Here’s a comparative summary of the benchmark results between Express.js and Fastify
Number of Requests
- Express.js: 90,840 requests/s
- Fastify: 231,809 requests/s
Time Performance
Express.js:
- Average request time: 5.47 ms
- 90th percentile: 6.55 ms
- 95th percentile: 7.37 ms
Fastify:
- Average request time: 2.1 ms
- 90th percentile: 2.56 ms
- 95th percentile: 3.51 ms
Data Transferred
Express.js:
- Data received: 21 MB (2.1 MB/s)
- Data sent: 7.3 MB (726 kB/s)
Fastify:
- Data received: 40 MB (4.0 MB/s)
- Data sent: 19 MB (1.9 MB/s)
Conclusion
Fastify shows significantly better performance compared to Express.js, processes about 2.5x more requests and have a lower avarage latency (2.1 ms vs 5.47 ms). Both frameworks handled all requests perfectly (0% failures)
In summary, Fastify demonstrates notably superior efficiency and processing speed in this test scenario.