Express vs Koa vs Hapi

2017/07/06

I’ve been using Express in almost all of my Node applications thus far,but for a recent project, I was asked to use a different framework - Koa. Then, in an open source project, we used yet another framework - Hapi.

I’ve seen each of the three before (and used Koa lightly for a small app in the past), but didn’t know much about their differences, or why a project should choose one over the others. So today, we’re going to whip up a few very basic servers and routes to check out the differences between the three.

Before we do that, though, let’s start with the basics:

What is a framework?

A ‘framework’ is a bit of a suitcase word - it can mean a few different things depending on the context and the application, but is always a supporting structure for an application. In Node, when we say ‘framework’, we’re talking about a structure that lets us build a REST API or otherwise make server connections without having to write a lot of the middleware involved.

So when we’re evaluating frameworks, we’re looking at:

How performant they are (Are they fast? Do they have a lot of middleware that we won’t use that might add bloat?)

What features they do and don’t support (Is async/await supported or built in?)

Do I (the developer) like them (this is wildly subjective, but hell, what’s software without strong opinions about subjective issues?)

Basic Comparision

Below is a quick summary of each framework, as well as really basic server code, so we can see their similarities and differences:

The above probably looks very familiar if you’re a node developer. We require express, and then instantiate it by assigning it to the variable app. Then instantiate a server to listen to a port, port 3000.

As an aside: the app.listen() is actually just a wrapper around node’s http.createServer(). You may have also seen it written this way:

Let’s look at Express routing. The code below is a paired down version of the routing we used for songs in HOPS Music, a side project I’ve been working on.

NOTE: We’re using MongoDB and Mongoose for HOPS Music, so some of the methods below (.save(), .find(), etc) are Mongoose, not strictly Express. I’ve also removed some methods (like .lean()) to add readability here:

Right away you can see the similarities. Essentally you just required koa instead of express. We even have the same app.listen() wrapper function.

What makes Koa truly standout - and what actually led to me working with it - is its way to ditch callback completely by either using ES6 function generators or the newer async/await control flow. It also eliminates much of the middleware that Express uses.

Generators are new to JavaScript, but had been used in other languages; one person comapred them to interruption in C. Generators introduced a means to run -> halt and run something else -> come back.

Koa 1 is famous for supporting generator-based control out of the box, at a time when most frameworks didn’t. This is what a typical piece of code for Koa 1 that uses the middleware cascading and improved error handling looks like:

I was confused, however, when I was reading the Koa docs and didn’t see any generators in the documentation. It turns out that Koa 2 (the current version) removes built-in support for generators and uses async functions instead. The signature of middleware functions will change to support async arrow functions.

Now here’s a version of what routes might look like in Koa2, using async/await (NOTE: It was amazing how few examples or demos I could find of this! I hope this is helpful to anyone looking for very basic REST examples in Koa2.):

I put this fairly crappy code out there as a first attempt at understanding Koa’s routing using the async/await control flow, rather than the generator control flow. The handling is fairly different; I thought Koa’s docs did a nice job of stating the difference for Express users:

Express, on the other hand, augments node’s req and res objects with additional properties and methods and includes many other “framework” features, such as routing and templating, which Koa does not.*

Hapi

(As a quick aside: Hapi is the server that I have worked with the least and the one that I had to do the most reading about. I’d be happy to be corrected or have clarification on anything written here.)

Hapi is a bit more robust than either Express or Koa. It’s a “feature rich framework” (as described in the docs) that favors configuration over code and attempts to cover a wider ranger of use cases out of the box. It was originally created by a member of WalmartLabs, and it is intended for large teams and large projects. Because of this, it can be a bit boilerplate-heavy for small projects.

Right away, you can see unique things in Hapi’s code. Look at the first few lines: hapi is required, but instead of instantiating a hapi app, you create a new Server and specify the port. In Express and Koa, we get a callback function; in Hapi, we get a new server object.

When we call server.start(), we start the server on port 3000 which then returns a callback. It’s not a wrapper around http.CreateServer(); it’s using it’s own logic.

Where we can really see the differences between Hapi and the other two frameworks, though, is in the routing. Let’s write some basic REST routes for a fake music REST API, similar to Express above:

You can see right away that, for each API call, we have to write a lot more code than we had to write with Express. Hapi’s configuration-centric approach does tend to mean more boilerplate, and that can make it more error prone.

That’s not to say that more boilerplate is always a bad thing; for larger teams, for example, Hapi may be more consistent and self-descriptive and thus easier to read and parse. I also know a several Node developers who like to have the extra control over their code, and so prefer to write out the methods and handlers this way. I don’t think it’s personally for me, but to each their own!

Conclusion

In summary:

Express: Lightweight and minimalist, supported by most middleware, seems to be the defacto standard.

Koa: Great async/await support for asynchronous flow. Seems to be ‘the new hottness’.

Hapi: Great for larger scale projects out of the box, but has some extra middleware to deal with.

I see myself continuing to use Express in the future, but would really like to do more with Koa, as I really like the new async/await control flow and see us generally moving in that direction. In the future, I’d also like to explore Socket.io, Sails and Meteor, which all seem to be quite popular.

Bibliography:

If you’d like a few more resources on the difference between Node frameworks, here are a few articles that I read while writing this blog post. Thanks to the authors for the great writeups!