In recent years, Node.js has consolidated itself as one of the leading platforms for developing web servers, primarily driven by Express. Express is widely used due to its ease of use, extensive compatibility with middleware, and large community. However, as the development landscape evolves and new demands arise, it becomes necessary to rethink some aspects of web server architecture.

When deciding to create a new HTTP server for CMMV (my personal project), the obvious question was: “Why not simply use Express?” The answer to this is not so simple, but it comes down to a few key points.

Focus on Compatibility Instead of Performance

Express prioritizes full compatibility with all applications running on it while maintaining support for both old and new middleware. This means that any change to the framework must be extremely careful to avoid breaking existing implementations. As a result, code evolution is slow and conservative, as changes must go through long periods of evaluation and testing before being implemented.

Express was created more than 10 years ago and, even with updates, it still carries some architectural decisions that are not optimized for modern scenarios. Recently, while participating in community discussions, I noticed that some internal JavaScript functions used in constructing the Express request/response object are extremely slow, directly impacting performance under high request loads.

Although alternatives like Fastify have emerged with a more aggressive focus on performance, introducing significant improvements such as an asynchronous hook system and the use of fast-json-stringify — which I already use in CMMV — they also come with an additional cost of complexity.

In the case of Fastify, for example, it is necessary to define explicit schemas for each JSON response, making code writing more bureaucratic. This trade-off between ease of development and performance may be valid depending on the maturity level of the team and the project, but in the context of CMMV, it represents an unnecessary obstacle. Since the architecture of CMMV is already based on contracts, where API structures and data are automatically generated, the requirement for additional JSON schemas would become redundant and negatively impact development agility.

Beyond the performance issue, there is an even more critical factor: the time and effort required to implement changes. Widely used frameworks like Express and Fastify need to accommodate different usage scenarios, making them flexible but also slowing their evolution or requiring extra configurations to function optimally in each case. In CMMV, the idea has always been to minimize the need for manual configurations and make the application structure as fluid as possible. The contract model already defines how the API should behave, without the developer needing to worry about schema creation or additional configurations. This means that using a generic web server would be a waste of resources, as it would introduce unnecessary layers of processing and configuration.

Given this scenario, it became clear that the best solution was to develop an HTTP server optimized specifically for CMMV. This server was designed from the ground up to be lightweight and efficient, eliminating unnecessary layers and ensuring a minimal request-response cycle. In addition to offering native support for WebSockets and RPC—something essential for CMMV’s communication—it integrates directly with Protobuf, guaranteeing fast and efficient binary communication. The request and response processing has been optimized to avoid unnecessary allocations and reuse objects whenever possible, reducing overhead and increasing scalability. The result is a server that meets the project’s needs without compromising simplicity and flexibility.

Express and Fastify remain excellent solutions for various applications, but neither was designed with the CMMV model in mind, which is based on API structure automation and the elimination of manual configurations. Creating a custom HTTP server was not a decision made to “reinvent the wheel,” but rather a necessity to ensure that CMMV maintains its vision of lightness, modularity, and high performance. In upcoming articles, I intend to share the challenges faced in developing this server and how we managed to make it the best option for the CMMV ecosystem.

Challenges in Creating the HTTP Server

The development of an optimized HTTP server for CMMV brought a series of challenges, from defining the base structure to optimizing each critical component to ensure maximum performance. To avoid completely reinventing the wheel and maintain a familiar design for developers accustomed to popular frameworks, I used Express, Fastify, and Koa as references. However, all the code was rewritten in TypeScript, prioritizing efficiency and flexibility.

One of the first challenges was ensuring a lightweight and modular structure without sacrificing compatibility with widely used standards. To achieve this, the implementation of key functionalities followed a model very similar to Express, allowing the use of middleware and header manipulation in the same way developers are already familiar with. However, some fundamental components had to be rebuilt from scratch to avoid the performance bottlenecks present in Express.

Optimization began with object management and instance creation. Whenever possible, instead of instantiating new classes using new, I used Object.create(null), which is more efficient because it avoids unnecessary prototype creation and inherited methods. This approach was applied primarily to the internal request and response structures, reducing garbage collector overhead and improving object reuse.

For routing, I used Fastify’s find-my-way, which proved to be significantly faster than Express’s router. This change allowed route resolution to be performed more efficiently by using a compact tree structure to quickly locate the corresponding route, instead of traversing a list of middleware like Express does.

Another critical point was defining the request lifecycle. Instead of following Express’s traditional approach, I opted for a hook-based system and asynchronous processing to better leverage Node.js’s event loop, which has evolved considerably in recent years. This decision ensures that each request goes through different processing stages efficiently, making full use of the runtime’s asynchronous capabilities. In many cases, I also used symbols to store internal request and response properties, reducing collisions and improving structural integrity, similar to Fastify’s implementation.

Even while aiming for compatibility with Express’s syntax, some structural differences were inevitable. The behavior of certain functions, such as server-static and default middleware, cannot be identical to Express without an additional compatibility layer. To mitigate this issue, I rewrote the main Express middleware directly into the CMMV server, ensuring that essential functions like etag, body-parser, cookie-parser, compression, helmet, server-static, and CORS work in an optimized and natively integrated manner. This approach allows the server to remain lightweight and fast without relying on Express’s legacy architecture while maintaining a familiar ecosystem for Node.js developers.

Testing was also a crucial part of the development process. To ensure stability and prevent regressions, a large portion of the CMMV server’s tests are the same ones used in Express. This guarantees that core behavior remains predictable, even with internal structural modifications.

Throughout the process, I realized that creating an HTTP server from scratch requires a delicate balance between compatibility, performance, and flexibility. The decision to rewrite critical components and optimize each step of the request processing pipeline resulted in significant performance gains but also posed challenges in integrating with existing middleware. Development is still ongoing, with more middleware being re-implemented and continuous improvements in routing efficiency, request handling, and memory management.

“The Cherry on Top”

For nearly four months, I worked intensively not only on developing the CMMV HTTP server but also on various other modules of the project to build a complete ecosystem. Today, I am finally releasing the beta version of CMMV at https://cmmv.io. Although it has not yet been extensively tested in production, the project’s documentation site already uses this server and framework as its foundation, allowing me to evaluate the application’s behavior in a real-world environment.

One of the most significant additions in the latest server version, 0.9.4, was a feature that, for the first time, allowed it to outperform Fastify. This improvement was achieved through the implementation of a short-term in-memory cache, a concept I have used for years in other applications and one that has proven to be extremely efficient.

In a real-world scenario, we never know when an application might experience a sudden surge in traffic or even a DDoS attack. Even with traditional caching layers such as CDN, Redis, or intermediary caches between the database and the controller, the application still has to handle these requests, resolving routes, processing incoming requests, and generating response headers. Each of these steps adds a small latency, which, when accumulated, can significantly impact scalability.

To mitigate this issue, I implemented complementary caching layers in one of my previous companies, beyond the traditional solutions. Essentially, when an application receives a large number of requests for the same route—such as the homepage of a website or frequently accessed API endpoints—it is highly likely that the same content is being served to multiple users within a short time frame.

With this in mind, the solution was to implement a short-term in-memory cache with a 2 to 5-second duration, storing the request result before it reaches the controller. This cache significantly reduces the need for:

  • Route resolution in the application’s router, saving processing power.
  • Queries to external services, such as Redis or databases, avoiding TCP connection latency.
  • Rebuilding response headers repeatedly, since stored responses already include this information.

Because this cache has an extremely short lifespan, it does not cause issues with data freshness. This approach is particularly effective for GET requests, such as HTML content, rendered views, and public API endpoints, where data does not change every second.

To make this optimization possible, it was necessary to develop an efficient cache key system, avoiding collisions and ensuring that each stored entry correctly represents the expected content. After extensive testing with various hashing algorithms, I chose MurmurHash3 for fast, low-collision hash generation, combined with URLSearchParams to normalize request parameters. This combination allows storing the most frequently accessed responses in memory efficiently without compromising data consistency.

The results of this implementation were remarkable. In internal benchmarks, the new CMMV server not only outperformed Express but also showed better performance than Fastify in scenarios where the cache layer was activated. This breakthrough reinforces the idea that optimizations tailored to a specific context can deliver significant performance gains without necessarily relying on a generic framework that imposes rules and structures not always ideal for every application.

Conclusion

Although projects like Express and Fastify are widely used and have stood the test of time, benefiting from a large community that has refined their code for decades, the reality is that any change in these frameworks takes a considerable amount of time to be implemented. Even when there is clear evidence of security or performance issues, the process of adaptation and correction can take months or even years, due to the need to maintain backward compatibility with legacy applications and the complexities involved in each update.

I am not suggesting that anyone should immediately replace Express or Fastify with the CMMV solution. The goal of this article is not to propose a new universal standard but rather to provoke reflection on how the choice of stack can directly impact an application’s infrastructure. Many times, a widely adopted framework may be hiding operational costs that are not immediately noticeable.

In the latest tests, it became clear that well-planned optimizations can drastically reduce CPU and memory consumption, minimize latency, and increase application scalability without requiring additional hardware investment. Small details—such as router response time or the need for multiple caching layers—can have a direct impact on the final infrastructure cost.

The development of the CMMV HTTP server was a challenging but extremely rewarding process. Building an optimized solution with a leaner request lifecycle, efficient routing, in-memory caching to mitigate traffic spikes, and compatibility with modern development concepts has opened new possibilities for a more performant and scalable server model.

The project continues to evolve, and the beta version is already available at https://cmmv.io for those who want to test and explore its functionalities. Over time, adjustments will be made, new modules will be added, and community feedback will be essential in refining this approach.

Ultimately, there is no one-size-fits-all solution for every problem. Understanding the strengths and limitations of each technology can be a key differentiator in building efficient and sustainable systems. 🚀

Benchmarks

  • https://github.com/fastify/benchmarks
  • Machine: linux x64 | 32 vCPUs | 128.0GB Mem
  • Node: v20.17.0
  • Run: Sun Mar 16 2025 14:51:12 GMT+0000 (Coordinated Universal Time)
  • Method: autocannon -c 100 -d 40 -p 10 localhost:3000

Version
RouterRequests/sLatency (ms)Throughput/Mb
barev20.17.051166.419.199.13
cmmv0.9.446879.221.038.40
fastify5.2.146488.021.198.33
h31.15.134626.228.376.18
restify11.1.034020.628.886.07
koa2.16.031031.031.725.53
express5.0.112913.676.872.30

Fontes: