This article begins a series of deep dives exploring the practicalities and pitfalls of using the NestJS framework for building modern Node.js server-side web applications. This first installment provides an overview of NestJS, discusses performance, and wraps up with thoughts from a developer perspective.
What is NestJS?
NestJS is an opinionated server-side Node.js framework that provides an out-of-the-box application architecture for creating highly testable, scalable, loosely coupled, and easily maintainable applications.
Just how opinionated is NestJS?
It follows the “convention over configuration” software design paradigm, often allowing developers to create more things in less time. This paradigm advocates guiding developers to use specific tools and coding approaches to decrease the number of decisions developers have to make without necessarily losing flexibility or repeating code.
That sounds great, but no framework is a perfect fit. Let’s look at some benefits and drawbacks.
- Low memory footprint
- Can handle a lot of concurrent requests
- Prefers the use of TypeScript
- Provides Dependency Injection
- Encourages modular organization
- Provides a command-line interface tool to help initialize, develop, and maintain your application
- Provides support for the most popular extensions like Express.js, Fastify, TypeORM, Passport.js, Apollo GraphQL, Redis, etc.
- Excellent free documentation and paid video courses
- Enterprise support available
- Uses a Single Threaded Event Loop Model architecture
- Less performant when servicing requests that take too long or process CPU-intensive tasks
Out of these concerns, performance may be the most important. Let’s take a look at how NestJS performs.
How does it perform?
To answer this question, we’ll look at how NestJS handles the total number of requests per second and the average response latency for 64, 256, and 512 concurrent requests. The Web Frameworks Benchmark comparison tool provides a click-and-point solution to visualize performance between different frameworks when running equivalent applications.
Let’s compare the performance of NestJS configured with the default Express web framework, Java Spring Boot, and C# ASP.NET Minimal API.
The charts above show that NestJS with Express cannot compete with Java Spring Boot and C# ASP.NET Minimal API when using an out-of-the-box configuration.
To address performance, NestJS recommends switching from the default Express web framework to Fastify. This switch is straightforward and should have minimal impact on your code. The charts below show the Total Requests per Second and the Average Latency after switching to NestJS with Fastify.
Wow. That’s a huge difference.
You could integrate caching or run more instances to increase performance further. Those approaches require additional effort or expense.
Regardless, you’ll never see NestJS perform better in every scenario.
Example REST Microservice
To demonstrate a basic NestJS application, I’ve provided an example using TypeORM and Swagger that implements a simple Express REST microservice. You can find the application source code on my GitHub Account.
This example application provides REST endpoints to retrieve and manage user records from a database. The endpoints allow you to get a list of all users, get one user, create a user, update a user, and delete a user. All client input, like user identifiers or details, is validated.
Time can vary depending on the team’s skillsets and project functionality. In this instance, it took less than 4 hours to put together this REST microservice with a team consisting of only myself.
Code development took one and a half hours – including database design, application functionality, Swagger configuration, and the database seeding script.
My effort for writing tests and documentation took two hours. Setting up data structures to mock database calls from scratch consumed most of the time here. Ideally, I’d leverage or share internal code to minimize the effort.
Overall, the development experience was enjoyable and has been for other larger projects I’ve done. Time generally spent on boilerplate code was minimal since NestJS provided a lot of the glue and suggested patterns for me already. Overall, NestJS saved a lot of time, and I could quickly knock out a working project.
The addition of Dependency Injection and easy integration with popular libraries are excellent features that saved me a ton of time. The modular architecture lends itself well to a high level of code reuse and a lower maintenance cost.
NestJS would be a good fit for teams responsible for the full stack: simultaneously developing the backend and a React/Angular frontend. Using the same programming language on both sides expands who can do the work and reduces the bottleneck of needing specialized programming skills.
One of the few situations where NestJS left me wanting was during testing. The absence of an in-memory SQL Server compliant database for unit testing made me sad, and I spent a lot of time building mocks for the database calls. While there are advocates for not including database calls in unit tests, I prefer to include them.
The “Single Threaded Event Loop Model” architecture of Node.js can hinder NestJS application performance. While specific approaches can alleviate performance problems, NestJS may not be the right fit for all high-traffic or CPU-intensive web applications.
However, my experience is that not all applications need to scale to Amazon-like performance to create business value. Sometimes, the winners are the languages and platforms that can exploit existing expertise or enable organizations to build more things faster. NestJS is an excellent choice for projects where it makes sense. It should be in your arsenal of tools to call on.
About Mission Data
We’re designers, engineers, and strategists building innovative digital products that transform the way companies do business. Learn more ↗️.