Otel showdown: NestJS vs ExpressJS

DeveloperSteve
Lumigo
5 min readFeb 28, 2024

--

Nest vs Express comparison using openteletry

Through years of developing applications, prototypes, and demos, I have come to appreciate the importance of robust development frameworks in enhancing Speed to Delivery Time (SDT), a critical metric in the fast-paced software industry where the ability to deploy scalable and maintainable applications quickly sets exceptional projects apart.

Frameworks are vital beyond initial development, encompassing crucial aspects like optimization, debugging, and monitoring, which are all essential in an application's lifecycle. An observability framework such as OpenTelemetry (Otel) proves invaluable in these areas, enabling distributed trace tracking to help improve application performance and issues.

Lately, developer communities have had lively discussions comparing NestJS and ExpressJS. I decided to conduct a comparison by deploying identical applications in a container alongside OpenTelemetry.

Demo Apps Deploy

In our exploration to dissect and understand the core functionalities and error management capabilities of NestJS and ExpressJS, we constructed demo applications that would serve as a real-world representation of common development scenarios. The creation process involved setting up basic yet functionally rich applications in both frameworks, each designed to mirror typical web application patterns, with routes crafted to simulate standard operations and intentional error conditions.

For the ExpressJS application, we began by laying the groundwork with essential routes: a welcoming home route to ensure basic functionality, a division operation to test numerical computations and error scenarios like divide-by-zero, and an asynchronous data retrieval route to mimic real-world database interactions, intentionally set to fail to observe error handling patterns. Similarly, the NestJS application was structured with parallel routes, allowing us to compare the handling of identical tasks directly across both frameworks.

These routes were carefully chosen to show how each framework navigates error handling. For example, the division route in both applications was a litmus test for observing the frameworks’ default behavior in handling mathematical errors. On the other hand, the asynchronous data retrieval route was designed to simulate a common backend operation with a built-in failure mechanism, providing insights into each framework’s approach to managing and reporting asynchronous errors.

Merging these applications into a single containerized environment posed its own set of challenges and learning opportunities. The objective was to maintain the distinct operational environment for each framework while ensuring they could coexist without port conflicts or environmental variable clashes. This setup was instrumental in simulating a microservices architecture where different services, possibly written in different frameworks, coexist within a larger ecosystem. The Dockerfile configuration was meticulously crafted to initiate both applications simultaneously, assigning them unique ports and setting up environment variables to distinguish between them, particularly for observability purposes with OpenTelemetry.

Here is what the Dockerfile looks like:

FROM node:20

WORKDIR /usr/src/app

COPY express-demo/package*.json ./express-demo/
COPY nest-demo/package*.json ./nest-demo/

RUN cd express-demo && npm install
RUN cd nest-demo && npm install

COPY express-demo/ ./express-demo/
COPY nest-demo/ ./nest-demo/

ENV OTEL_SERVICE_NAME=NestVsExpress

EXPOSE 3000 3001

CMD sh -c ‘cd /usr/src/app/express-demo && export OTEL_SERVICE_NAME=”express-app” && NODE_OPTIONS=”-r @lumigo/opentelemetry” node app.js & cd /usr/src/app/nest-demo && export OTEL_SERVICE_NAME=”nest-app” && NODE_OPTIONS=”-r @lumigo/opentelemetry” npm run start’

How They Measured Up

With our demo applications now fully operational and equipped with OpenTelemetry through Lumigo’s integration, we are ready to dig into the fun part of this comparative study. This phase is crucial, as it tests the operational efficiency and error-handling capabilities of NestJS and ExpressJS and offers insight into their performance through detailed metrics and trace data provided by Lumigo.

- Homepage (`/`): Accessing the root of both the ExpressJS (http://localhost:3001/) and NestJS (http://localhost:3000/) applications served a simple “Hello World!” response. This basic interaction served as an initial test of responsiveness and operational status for both frameworks.

- Divide Function (`/divide`): We introduced a division operation in both applications (http://localhost:3001/divide?num1=10&num2=0 for ExpressJS and http://localhost:3000/divide?num1=10&num2=0 for NestJS), deliberately omitting error handling for division by zero. This setup allowed us to examine how each framework naturally responds to such common programming oversights and how errors are reported through Lumigo.

- Asynchronous Data Fetch (`/data`): Both applications featured a route to simulate a failed asynchronous operation (http://localhost:3001/data for ExpressJS and http://localhost:3000/data for NestJS). This scenario aimed to showcase Lumigo’s capability to trace asynchronous errors, shedding light on each application’s approach to managing failed async processes.

What stood out in our findings was how ExpressJS and NestJS handled the simulated timeout error in the asynchronous data fetch scenario. The comparison highlighted the differences in error management between the two frameworks. It underscored the depth of visibility Lumigo provides into such events, offering a granular view of the error-handling mechanisms and their implications on application performance.

Test it out with your NodeJS Deployments.

Exploring the intricacies of error management and performance between the ExpressJS and NestJS frameworks has shed light on the distinct paths each takes towards application development. ExpressJS is known for its straightforwardness and adaptability, while NestJS follows a more regimented, convention-based structure with sophisticated exception handling mechanisms. This investigation into their operational behaviors under stress and their approach to exceptions has emphasized the critical impact that framework selection can have on an application's robustness and operational efficiency.

If you are interested in finding out more about how your stack measures up, sign up for a free lumigo account and test it out for yourself. It's fast and easy to setup, and it too can be your stack saver. Additionally, if you want to find out more about the demo apps used within this blog post, check out the post comparing NestJS and ExpressJS to see the full code, container config and more.

--

--

DeveloperSteve
Lumigo

Lilypad Network Chief Innovation Officer | Director The Coochin Company | 30+ years Developer | 10+ years Data Analyst | 10+ years Devrel