Performance Benchmarks

Independent, verifiable performance testing using industry-standard tools. MoroJS ships its own native C++ engine (@morojs/engine) as the default transport — measured here from the published npm packages. All benchmarks are reproducible with one command.

Moro Engine

Moro's own native C++ engine (@morojs/engine), the default transport, zero config.

Requests/sec102,409
Avg Latency0.9ms
Pipelined ×10572,053
Default · Peak Performance

• Native C++ engine, same Moro API

• Beats uWebSockets.js in both profiles

• Lowest latency (p99 1.6ms)

Clustering Mode

Built-in clustering with auto worker management.

Requests/sec103,971
Avg Latency0.9ms
Pipelined ×10584,016
Multi-Core

• Same native engine × auto workers

• SO_REUSEPORT load distribution

• Scales across all CPU cores

Node.js Path

Pure Node http server, the automatic fallback wherever a prebuilt engine binary isn't available.

Requests/sec68,163
Avg Latency1.4ms
Pipelined ×10119,570
Zero-Native Fallback

• No native binary required

• Automatic, nothing fails to boot

• Matches raw node:http throughput

Framework Comparison

Every framework measured on the same machine, same session, same tool no cross-sourcing. Two profiles: real-world (no pipelining, the headline how real HTTP clients behave) and pipelined ×10. Full data + methodology in the benchmark repo.

FrameworkReq/sec real-worldPipelined ×10LatencyRuntime
MoroJS - clustered (engine × all cores)103,971584,0160.9msNode 24
MoroJS - Moro Engine (default)102,409572,0530.9msNode 24
MoroJS + uWebSockets.js (opt-in)101,237520,7990.9msNode 24
Elysia96,71718,6901.0msBun
Fastify69,375117,0691.4msNode 24
MoroJS - Node.js path (fallback)68,163119,5701.4msNode 24
Elysia (Node adapter)66,786115,7981.5msNode 24
Koa60,92493,9031.6msNode 24
Hono56,926100,2781.4msNode 24
Express47,27969,5402.1msNode 24
vs uWebSockets.js+9.8%572k vs 521k · pipelined
vs Fastify+48%102k vs 69k · real-world
vs Express+117%102k vs 47k · real-world
vs Elysia (Bun)31×572k vs 19k · pipelined
Every row measured on one machine, one session (Mac Studio M2 Ultra, Node 24.11, wrk, best-of-3, both profiles). Reproduce any of it with node bench.js <target> --pipelined.

Key Findings

Moro Engine vs uWebSockets.js

Pipelined ×10 (through MoroJS)+9.8% faster
Real-world profileAhead in both
Bare engine vs bare uWS (peak)664k vs 648k

Moro Engine vs Node.js Path

Real-world Throughput+50% faster
Pipelined Throughput4.8x
Latency Reduction~36% lower

Detailed Performance Metrics

Moro Engine (default)

Real-world req/s: 102,409
Pipelined ×10 req/s: 572,053
Latency avg: 0.9ms
Latency p99: 1.6ms
RSS under load: 230 MB

Clustered (engine × all cores)

Real-world req/s: 103,971
Pipelined ×10 req/s: 584,016
Latency avg: 0.9ms
Latency p99: 1.6ms
Workers: auto (24)

Node.js Path (fallback)

Real-world req/s: 68,163
Pipelined ×10 req/s: 119,570
Latency avg: 1.4ms
Latency p99: 1.8ms
RSS under load: 216 MB

Testing Methodology

Tools & Environment

  • wrk (cross-checked with oha, bombardier)
  • Mac Studio M2 Ultra, idle machine
  • Node.js v24.11.0
  • Published npm packages, production config

Test Parameters

  • 100 concurrent connections
  • 40 second duration, best-of-3 per target
  • Both profiles: real-world + pipelined ×10
  • One server at a time, cooldowns between

Test Configurations

  • Moro Engine — default (port 3117)
  • Clustered mode (port 3111)
  • Node.js path (port 3110)
  • Boot sanity asserts the intended engine

Straight talk

These measure framework overhead, not your app. Your real numbers ride on your database, your I/O, and your business logic. What they do prove: MoroJS adds almost nothing on top of the transport it runs on. And every row is one command to reproduce - so don't take our word for it, run it.

Reproduce These Results

Clone & Setup

git clone https://github.com/Moro-JS/benchmark.git

cd benchmark && npm install

One command per target — no second terminal

# Moro Engine (the default) both profiles, best-of-3

node bench.js engine --pipelined

# Clustered / Node path / competitors

node bench.js cluster single fastify express koa --pipelined

# Everything, saved to a results-<timestamp>.md

npm run bench:save

How the harness works

Each run boots the server, waits for readiness, asserts the intended engine actually loaded, runs the load generator (a separate native process), samples memory, tears down, and cools down before the next target.

Quick sanity runs: npm run bench:quick (10s — never published). Raw baselines: node bench.js raw-engine raw-uws raw-node.