How to Choose a Node.js Framework

Jeff Morhous headshot

Jeff Morhous

@JeffMorhous

Building a Node.js server without a full framework is technically possible, but leaning on a framework will certainly improve your development speed and likely your app’s quality. Frameworks give us reusable tools that help us not have to reinvent the wheel for every little thing. Choosing a Node framework (and picking the right one) will ensure you spend less time on basic functionality and more time on your actual application logic.

Comparing Node.js frameworks

The popularity of JavaScript and Node.js means we get a handful of really incredible frameworks to choose from and plenty of great hosting options. In this article, I’ll explain what makes each framework unique and how you can choose between them. Ready to jump in with me?

Using Express.js

Express is the common standard for Node.js web frameworks. It’s the Node framework that nearly every Node developer has used or at least heard of. It’s a small framework that doesn’t impose a strict structure. This makes it pretty flexible. You can use Express to build everything from simple JSON APIs to full-blown web applications. Even if you don’t choose to build on Express, the framework you do choose may include it under the hood.

Expressjs is a common framework meme

Getting started is very easy, as Express intentionally has a very shallow learning curve. If you’re new to Node, Express is often the first recommendation from the JS community. I put a HUGE emphasis on developer experience. It’s why I love Rails. The Express developer experience is straightforward, but it’s not an all-inclusive framework like Rails or even Laravel.

One of Express’s biggest strengths is its huge ecosystem of middleware and plugins. Because it’s been the most popular Node framework for years, almost any feature you need has an Express middleware available. This points out an issue. Express does not have all that much without this middleware, so you’ll be forced to make lots of choices for your app!

One important thing to keep in mind is that Express is not the fastest framework out there. It’s known to have a bit of overhead in its request handling. Other frameworks like Fastify or Koa can beat Express on throughput. Express is ideal when you want maximum flexibility and a quick start to a project.

Express has stood the test of time. It is mature, stable, and proven in production. Many high-traffic websites have been built with Express, an Express app can certainly scale to handle very large loads. Since Express leaves structure to you, it can scale organizationally if you apply good conventions. But you won’t get as much built-in support for scaling as you would from a more structured framework like Nest.

Using Fastify

Fastify is a newer player than to Express, but it has gained a lot of traction due to its performance. As the name implies, Fastify was designed for fast Node apps. This raw speed comes from an event-driven, highly optimized internal architecture with minimal overhead per request.

Fastify performance benchmarks

Fastify’s API and concepts will feel familiar to you if you already know Express. You still register routes and handlers, and you can also use middleware (though in Fastify they’re called hooks or plugins). The learning curve is moderate, certainly more than express. This is because it’s a bit more structured than Express, pushing you to define JSON schemas for your requests/replies that can be used for validation and serialization. This schema-based approach is optional but strongly recommended for the framework as it helps catch errors and improve performance.

Fastify does have an official plugin system with many plugins available to add features. The documentation is solid (I read tons of documentation), and the community is growing fast.

Performance is Fastify’s headline claim to fame. It consistently ranks among the top Node frameworks in terms of throughput and low latency. In a real-world context, this means a Fastify server can handle more concurrent requests on the same hardware than an Express server, which gives you room to grow.

If you’re autoscaling your Node app (you should be), Fastify’s low overhead is even more beneficial. Each instance can do more work, potentially reducing the number of instances you need and certainly making each scale event more granular. There’s no special magic beyond this. Because each process is more efficient, your scaling is more cost-effective.

A great use case is building APIs where performance is the most important requirement. Fastify stands out in use cases where every millisecond counts and in resource-constrained environments. The trade-offs are relatively minimal. Fastify isn’t as universally known as Express, and its community, while enthusiastic, is smaller than Express’s. That said, it’s not obscure – plenty of big companies use Fastify.

Using Koa

Koa is often described as the successor to Express. It was actually created by the same team behind Express, with the goal of making a smaller, more modern framework. Koa is even more minimalist than Express, shipping without any middleware (not even a router!). This sounds daunting if you’re used to something batteries-included like Ruby on Rails, but it’s by design.

Koa aims to strip the core down to just the essentials, leaving it up to you to add only what you decide you need. Koa’s core library is tiny, only spanning around 600 lines of code. It leverages modern JavaScript features like async/await, which allows Koa to avoid the legacy of callback-based middleware that Express 4 uses.

If you already know Express, picking up Koa is straightforward (conceptually, at least). You still create a server, define routes, and use your chosen middleware. Many developers find Koa’s usage of async functions makes middleware composition and error handling simpler than Express. The learning curve is moderate, more of a challenge than the other Node frameworks we’ve discussed so far. You will need to assemble your own stack of middleware, which means reading documentation and doing a proper implementation for those pieces. Koa intentionally does not maintain compatibility with Express-style middleware, so you can’t just drop in an Express middleware and expect it to work.

Koa’s lean design does have performance benefits. With less overhead, Koa can handle more requests per second than Express in comparable scenarios. In practice, Koa’s performance is pretty good for a Node framework, but not as good as Fastify.

Koa is a great choice if you want a modern, minimal framework and are comfortable assembling all the pieces you need. It’s well-suited for building APIs or web services where you might otherwise use Express, but you’d prefer a cleaner async/await-based flow. It’s like Express but faster and a bit more refined, at the cost of doing a little more setup on your own.

Using Nest.js

NestJS is a different paradigm from Express, Fastify, or even Koa. Nest is a full-featured, batteries-included framework. The idea behind Nest is to give Node applications a structured architecture out of the box. It adopts a modular approach, with support for TypeScript and features that will remind you of fuller frameworks like Rails or Laravel.

Nest.js features

Working with Nest can be a mix of excitement at what’s included and dread all there is to learn. The developer experience is driven by the fact that NestJS forces a structure. Personally, I love this, but I’m already a huge fan of convention over configuration. It also makes Nest great for large teams since everyone knows where to put their code and how things should be structured.

The framework comes with a CLI that can generate boilerplate, just like Rails scaffolds. However, the learning curve is steep. You need to get comfortable with lots of concepts before becoming productive, since they affect the overall flow of Nest apps.

A NestJS application uses the Express framework under the hood. This means that Nest’s performance will be somewhere in the ballpark of Express, maybe even a bit slower due to the overhead. Still, you can swap out the underlying HTTP adapter to use Fastify instead of Express. With a one-line change, your Nest app can run on Fastify and instantly get a big boost in throughput.

That said, pure performance is usually not the deciding factor in choosing to use Nest. Most people who choose Nest do so for its organization and convention. It shines in large, long-term projects where maintainability is key.

NestJS does not include a frontend framework. It is purely a backend framework, but it can serve a frontend if you want it to. Since it’s just a Node app (built on Express or Fastify), you can add a view engine like Handlebars to render server-side HTML templates.

NestJS does not ship with a built-in ORM, which makes me sad! It does, however, cleanly integrate with whatever data access layer you prefer. The community has produced official and semi-official integrations including TypeORM and Primsa.

If your project will be worked on by many developers, or you foresee it growing significantly in scope, Nest provides the architecture to manage that complexity. It’s also great if you prefer strongly typed code and are a fan of Angular or Java-style frameworks. Many companies choose Nest for mission-critical apps because it imposes a consistent structure and has many reliability features built in.

Using Next.js

Node.js is also used in full-stack frameworks that integrate the frontend and backend together. Next.js is a framework for building web applications with React (frontend) and Node.js (backend) together, but it’s best described as a frontend-first option. With Next, you can render React pages on the server (SSR), generate static sites, and define serverless API routes all in the same project.

Next.js is a Node.js framework

I have a love/hate relationship with Next. It has a generally good developer experience, especially for frontend engineers who want to expand into backend without leaving the React ecosystem. Next handles all the build tooling, code splitting, etc. The local development experience is genuinely great, boasting hot reloading, actually useful errors, and so on. Still, the learning curve Next is moderate. You need to know React, and then learn Next’s conventions. That sounds simple, except those conventions change in breaking ways relatively often. It’s not uncommon for me to visit an old Next project and find it needs lots of work to bring up to date.

Next.js is best for scenarios where you need to build a frontend interface along with your backend logic. It allows for server-side rendering (SSR), which means pages are pre-rendered on the server for fast initial load and SEO, as well as static site generation (SSG) where pages can be precomputed at build time and served as static files.

Since Next is a frontend-first framework, it does not provide an ORM either. If you need an ORM, you add it yourself just like you would in a plain Node.js app.

It blends the lines between a frontend framework and a backend framework, which may be a selling point or a problem depending on your perspective. That said, for many teams, the productivity gain outweighs these concerns. Next.js is often recommended for full-stack apps because it offers great performance to the end user and fairly easy scalability (if you deploy to a platform like Vercel, and it scales automatically).

How to make the right decision

Choosing the best Node framework for your project is hard. Of course, it depends on what you’re building and what your priorities are. It’s not one-size-fits-all. Still, I’ll do my best to give you a straightforward decision-making framework.

Node frameworks compared

For quick prototypes of simple APIs, just use Express. Its simplicity is unbeatable for getting something off the ground fast. You won’t waste time fighting the framework or learning new conventions.

For high-performance APIs, use Fastify. If you expect heavy load or just want to maximize efficiency, Fastify will give you more headroom. It’s a great choice for building JSON APIs or microservices where throughput is critical. The trade-off is a slightly smaller community and middleware options, but the core features are pretty good by themselves.

Koa is an option if you like to keep things lightweight and are interested in using something super modern. It’s a nice upgrade from Express when you want to embrace async/await and have more control. Choose Koa only when you are comfortable assembling your own toolkit and you want a framework that stays out of your way.

For large-scale, enterprise APIs, NestJS is often the best choice. If you’re building something that might grow to dozens of modules, with several teams working on it, Nest provides the architecture to keep it maintainable. It’s the go-to if you prefer strong conventions, TypeScript, and a full-featured backend framework that comes with everything included. It scales just like any other backend option and has a great structure that’s easy to lean on.

For applications that need a strong frontend component or you simply like having both parts of a project in one framework, Next.js is hard to beat. Naturally, the full-stack nature of the platform gives you plenty more options for your work than Nest or one of the backend options. If your project is essentially a React app that needs server-side rendering or will benefit from static site generation, Next.js will make your life much easier.

I couldn’t help but add Rails and Laravel to this chart to make the comparison even more clear. I’m quite attached to those frameworks’ batteries-included approach to building web apps. With either framework, you can build fully-featured CRUD apps that support user authentication and a dozen other common features, all without adding a single library. Next is the only Node framework that comes close to this amount of support.

That being said, all of these frameworks are perfectly capable of building useful, scalable applications. Just keep in mind that they offer different balance of convenience, performance, and structure. Good luck choosing!