An Opionated Guide to Node Frameworks in 2020

Spencer
Spencer
An Opionated Guide to Node Frameworks in 2020

Node.js white and green logo

In 2020, Node.js is now over 10 years old. There's a lot of great frameworks to choose from to power your apps into 2021 and beyond.

This is a rundown of the major Node.js web frameworks to choose from.

While I intend to give every framework it's fair shake, my opinion and perspective is mixed in here. I also make a recommendation about whether you should rely choose the framework to start a new project in 2021.

Quick picks

Here are my 5 quick-picks for a few different use-cases:

Express

website | github | 2021? πŸ‘

The reigning champ of Node frameworks. Rock solid, mature, well-known and the language standard for web frameworks.

There's a large number of community packages and middleware that are compatible with Express. It's flexible enough to support a JSON API or a template-driven web app delivering HTML.

If there's a con with choosing express, it's that it is unopionated - you can do anything including turn your server into a mess of spaghetti code. You'll need to chart your own path in terms of how you organize your codebase, as well devote development time to choosing, integrating, and maintaining 3rd party packages.

Under the hood, express is often wrapped by other frameworks (like loopback, feathers, and sails), which helps contribute to its overwhelmingly dominant download metrics on npm.

Loopback

website | github | 2021? πŸ‘

Currently in progress on version 4, which is written in TypeScript/ES2017.

Loopback benefits from the patronage of IBM via strongloop, and version 3 is robust and mature. Version 4 is yet to be seen.

In their own words ...

LoopBack is a highly extensible, open-source Node.js and TypeScript framework based on Express that enables you to quickly create APIs and microservices composed from backend systems such as databases and SOAP or REST services.

Conversely to Express, Loopback makes a number of architecture decisions and exposes a number of packages in order to accomplish things like routing and fetching data from a DB.

Loopback makes it easy to map models to RESTful API endpoints; this can make it pretty easy to scaffold out a data-backed API quickly.

Adonis

website | github |2021? πŸ‘Ž (wait for v5)

Adonis is heavily influenced by PHP's Laravel framework, and includes things like a Laravel-influenced IoC container, custom ORM called Lucid, service providers, and a bunch more.

Currently, Adonis is under a rewrite and looking to release version 5 which supports TypeScript and is a pretty substantial rewrite of the core.

Adonis comes with a lot of functionality out of the box (CSRF protection, file upload wrappers, migrations, etc).

Unfortunately, Adonis v4 can often feel like a port of Laravel and makes choices to reflect Laravel rather than do things that make sense in Node. One example is taking over the module system and exposing a global use command that taps into the IoC container; this feels like Laravel and PHP's module system rather than Node.

Adonis is a reasonable choice if you're looking for a batteries-included framework and are already comfortable with Laravel. You'll feel quickly at home coming from Laravel.

Sails

website | github | 2021? πŸ‘Ž

Sails intends to be a full-stack, MVC framework that is high-productivy and allows you to build fully-featured apps quickly. The name is a hat tip to Ruby on Rails, although Sails has not gained dominance in the Node space like Rails did in Ruby.

Sails was first introduced in 2012. It ships with ejs templates, and a custom database-agnostic ORM called Waterline, as well as the standard wiring for routes and controllers.

However, Sails makes some unusual decisions at it's core - the standard MVC approach is to declaratively map routes with handlers in controllers. Sails uses "actions" as it's core concept, which wrap up input / output validation as well as a handling function. Additionally, Sails makes extensive use of a rather flimsy concept called "helpers", which become globally available bound functions in the name of re-useable code.

Unless Sails really captures your imagination with its unique approaches, I wouldn't recommend adopting Sails in 2021. It's opinionated, but I'm not sure it's going to live up to its namesake.

Meteor

website | github | 2021? πŸ‘Ž

Meteor is a full-stack framework, and it intends to make it simple for developers to create data-backed applications. Notably, it offers a simple way to implement real-time syncing with changes in the database all the way to the client.

It is opinionated, and has created it's own ecosystem around the framework - all the way to offering a hosting service called Galaxy.

In my opinion, Meteor has taken a too opinionated line in a number of ways. Installing meteor bundles and ships with npm (so you run meteor npm), rather than the other way around. It introduces it's own package ecosystem called Atmosphere.

One of the more unfortunate opinions is that it chooses MongoDB; it's theoretically possible to use SQL but with lots of limitations. However, this opinion means that meteor may not be right for lots of applications, and it excludes a number of mature databases that might be a better fit for your use-case.

Unless you're ready to buy into the Meteor opinionated tack and ecosystem, it's probably not the right choice; however, if you're ok with the choices Meteor has made, it could be the right-choice for an application where real-time data sycing is important.

Derby

website | github | 2021? πŸ‘Ž

Derby intends to be a MVC framework for building real-time, collaborative web apps.

From their docs:

Derby includes a powerful data synchronization engine called Racer that automatically syncs data among browsers, servers, and a database. Models subscribe to changes on specific objects, enabling granular control of data propagation without defining channels.

In order to support the real-time aspect, Derby also comes with some strict opinions about the storage layerβ€”data is stored in MongoDB (or in memory) via ShareDB.

Derby's implementation allows you to build server-side rendered, real-time applications - so you don't need to manage client-side SPA frameworks (like React or Vue). It uses websockets to enable data syncing.

In addition to the DB, Derby makes some opinionated choices to enable the real-time syncing: Derby comes with it's own templating language and component framework.

Derby has been under development for about 5 years (although, surprisingly, still at a 0.10 release and not a 1.0 yet). It has not received dominant community adoption. Derby has made some really interesting tech choices, and the real-time syncing mechanism is certainly intriguing, but I'd proceed with caution before adopting for your next production application.

Koa

website | github | 2021? πŸ‘

Koa is created by the same folks behind express: same great team, new approaches. Express was created at a time before promises or async/await.

This allows you to use promise-based approaches to middleware:

app.use(async (ctx, next) => {
  await next();
  // happens *after* the response is set
});

The other notable difference is the use of a context object that encapsulates both the request and response, rather than receiving those as separate parameters. This makes the method signatures of middleware and route handlers a bit simpler.

If like express and promises, Koa is worth a try. It has the same unopionated approach but with some more modern tactics.

The only unfortunate aspect to Koa is that the ecosystem around express is still super strong and the de facto standard. So, for instance, middleware does not interoperate out of the box, so you'll need to use the koa-connect wrapper or find some koa specific middleware.

However, of all the frameworks on this list, Koa's has been steadily gaining traction in the past 5 years, topping over 600k weekly downloads in 2020. See the stats for yourself.

Hapi

website | github | 2021? πŸ‘Ž (but see reasoning below πŸ‘‡)

With a tagline of "The Simple, Secure Framework Developers Trust", hapi was born out of Walmart's challenges handling Black Friday-scale ecommerce surges.

Hapi (at least on paper) emphasizes their choices around security and maintainbility. As their name suggest, they prize developer happyness.

Hapi takes some hard stances that stand in direct contrast to express; for instance, "Middleware is just another name for bad design." source.

However, hapi hit a rough patch in 2020. The core maintainer, Eran, said he was abandoning the project due to lack of sustainable financial support. Shortly thereafter, a group of maintainers stepped up to form a technical steering committee.

It is too early to tellβ€”hapi may continue to live on happily ever after (sorry, but bad pun intended) or not. I wouldn't stake a new project on hapi in 2021 until it's more clear that it will continue to receive care from a team of maintainers.

Other than the lack of clarity about hapi's future, it provides a pretty mature and robust alternative to the approaches taken in express and koa. Worth a 2nd look to see if it jives with your style, especially if the technical steering committee proves itself in 2021.

Restify

website | github | 2021? πŸ‘

As you would suspect from the name, restify is about creating RESTful APIs.

In a lot of it's choices, it feels really similar to express β€” however, it has a couple of additions to make it easy to do complext things like version an API:

server.get('/hello/:name', restify.plugins.conditionalHandler([
  { version: '1.1.3', handler: sendV1 },
  { version: '2.0.0', handler: sendV2 }
]));

Restify is stable and mature; if what you need is a REST API, restify might just help you get there and solve some problems along the way.

Fastify

website | github | 2021? πŸ‘

Continuing the trend here with the name matching the goal of the framework - fastify's goal is to be the fastest web framework for Node.js.

If performance is your thing, then fastify is the best choice for you. (They even publish a prominent benchmarks page.)

Fastify includes some different choices, like routing with schema validation baked in (using JSON schema) and plugins instead of functional middleware - most of these come down to style.

In general, the framework takes an unoppinionated stance on most things but performance.

Fastify sometimes serves as the basis of other performance, conscious frameworks (e.g., you can opt-in to fastify in Nest).

Feathers

website | github | 2021? πŸ‘

Feathers is a wrapper around express's core routing capabilities that provides a number of additional features like services and hooks.

Feathers has a few major benefits:

  1. Protocol agnostic: Provides an abstraction layer over the protocol to provide services that can be accessed via HTTP, websockets, or just by calling methods in Node.
  2. Datastore agnostic: Loose coupling between a service and storage format (SQL, NoSQL, etc) via adapters.
  3. Built in real-time functionality via websockets (over socket.io)

Feathers has become a mature framework in the past few years, and it's datastore agnostic realtime functionality puts it ahead of either Meteor or Derby in my opinion.

There's a large-enough community and ecosystem that many (although not all) common application functionality is available.

There are a few cons:

  1. services can be merely a slim CRUD wrapper around a database schema (powered by mongoose, sequelize, or whatever). It's easy to bootstrap an API quickly, but evolving the API and storage tier separately can be challenging.
  2. Hooks - which provide some convenience over raw express middleware - tend to spread important business logic throughout the application, creating subtle dependencies between services, and making transactional boundaries difficult.

If you're a strong believer in layered architecture, feathers may not be for you.

Vanilla

website

Maybe you don't want or need a framework in the end - you can spin up a quick HTTP server using node's standard library:

const http = require('http');
const server = http.createServer(function (req, res) {
  res.write('Hello World!');
  res.end();
})
server.listen(8080);

Since all requests flow through a single callback, you'll quickly need a routing library that takes into account HTTP method, request path, etc - but for a basic web server with a route or two, you won't need anything else.

Honorable Mentions

Total.js

website

Total.js is a bit more old-school and ships with a lot of components.

(Honestly, I find their documentation super overwhelming and hard to understand.)

Next.js

website

Not exactly a node framework, but wildly popular in the React community for building server-rendered applications. It runs in node and serves up HTML before providing a way for React to take over as an SPA.

Something important missing?

Feel free to drop me a line at jones.spencera@gmail.com. Please mention the framework and send the key links and I'll update the post.