Overview
A native HTTP engine for Node.js has been developed with the goal of providing a faster, built-in alternative to the standard node:http module. This engine is designed to address performance bottlenecks and reduce the overhead of third-party native addons.
Performance Benchmarks
Benchmarks were conducted on a Node 24.11 environment running on an M2 Ultra machine. The testing tool was wrk with 100 connections over a 40-second duration, recording the best result of three runs. All packages were pulled from npm rather than built locally.
Requests per Second
| Server | Non-pipelined | Pipelined ×10 |
|---|---|---|
@morojs/engine |
105,974 | 663,735 |
| uWebSockets.js | 103,744 | 647,530 |
raw Bun.serve
|
107,119 | 21,686 |
raw node:http
|
69,045 | 109,538 |
| Hono | 56,926 | 100,278 |
Observations
- Non-pipelined Performance: The non-pipelined column appears to be limited by the test machine's maximum capacity, with native transports converging around 105k requests per second.
Bun.servemarginally leads in this metric. Adding a framework like Elysia on top ofBun.servereduces its performance to 96.7k requests per second. - Pipelined Performance: The new engine shows a significant advantage in pipelined requests, achieving about a 10% improvement over uWebSockets.js once a framework is integrated. This is attributed to response corking and batching pipeline requests into a single write operation.
A full performance matrix and testing harness are available in the benchmark repository.
Technical Architecture
The engine is built with a C++ core using raw V8 bindings instead of N-API. The primary design principle is to minimize boundary crossings between C++ and JavaScript.
- General-purpose bindings typically perform 10-20 JavaScript crossings per request.
- This engine reduces crossings to 2-4 per request by batching request snapshots and response writes.
Corking and Optimization
Corking is central to the performance gains, particularly for pipelined requests. Version 1.1.0 introduced batching of an entire pipeline into a single write, along with a zero-allocation hot path, resulting in a 3.7x improvement in pipelined performance over version 1.0.0.
Trade-offs and Build Strategy
- N-API vs. Raw V8: While N-API offers a stable ABI and easier maintenance, it did not achieve the desired performance levels. Raw V8 bindings were chosen for performance, at the cost of ABI-locking.
- ABI Compatibility: To manage the ABI matrix, the project builds and ships precompiled binaries from tagged CI builds with npm provenance. This ensures day-one compatibility with new Node.js releases, avoiding delays often associated with third-party native addons.
- Fallback Mechanism: In environments where a prebuilt binary is not available, the engine gracefully falls back to
node:httpand logs the reason viaapp.engine.fallbackReason.
Security
The engine has zero external dependencies. It handles query, cookie, multipart, and route-pattern parsing directly within the framework.
- Fuzzing: These parsers are property-fuzzed with a fixed seed on every push and nightly with a rotating seed at 500,000 iterations per property.
- C++ Parser: The underlying C++ HTTP parser has its own dedicated fuzzing harness within the engine repository.
Availability
This engine is the default server for a framework maintained by the author. The source code and project repositories are publicly available.
- Engine Repository: https://github.com/Moro-JS/engine
- Framework Repository: https://github.com/Moro-JS/moro
- Project Site: https://morojs.com
Top comments (0)