Thursday, 7 December, 2017 UTC

Summary

Chances are you've heard of Hapi by now. And you might be wondering how it compares to the Express web framework in Node.js development. In this article, we will compare the frameworks head-to-head and explore the differences in experience for the developer.

Similarities and Differences Between Hapi and Express

Both Express and Hapi aim to be highly flexible, simple, and extensible. This similarity means that both have easy-to-use APIs, they're highly modular, and can support your application as it grows potentially very large.

The learning curve for these frameworks, since they are quite straightforward, is low, unlike a more opinionated framework like Meteor. If you are coming from using Express you should be able to quickly pick up Hapi, and vice versa.

There are, however, some philosophical differences between the two frameworks, which we'll describe throughout this article.

Hapi.js includes more "batteries" by default than Express does. For instance, when parsing payload from forms to via "POST" requests, with Express, you have to include the body-parser middleware. You then use that to parse the POST payload and use the data from the user. On the other hand, with Hapi, you need no middleware. The payload is parsed for you by the framework itself, and you can access the payload directly on the request object. Little conveniences like that are replete in Hapi.

Basic Server and Routing

To get started we will create hapiapp, an example Hapi app that shows basic Hapi functionality. We will also create expressapp, a mirror image of hapiapp that uses Express so we can compare the frameworks side by side.

Both of our apps will use ES6 JavaScript.

Create two directories, hapiapp and expressapp.

Inside each, run the command:

$ npm init

Then accept all the defaults by pressing "Enter". This creates a package.json file inside each directory, thus creating our two different apps, hapiapp and expressapp.

First, let's see basic routing in action in Hapi.js. Inside the hapiapp directory, install the Hapi module by running the following command:

This basic app creates two routes at localhost:8000 and localhost:8000/hello/<name>. The first one will print out a simple "Hello World" to the user. The second, however, is a dynamic route that prints out "Hello" followed by the name we input in the browser after the second "/" slash.

From the hapiapp directory run the app using the following command and try out the URLs given above:

$ node index.js

The equivalent Express app involves a very similar setup. Inside our expressapp directory, install Express as a dependency using the command:

This accomplishes the same functionality as the Hapi app. To run it, from inside our expressapp directory, run the command:

$ node index.js

Visit the routes given above, except now we are running at localhost:3000 instead of localhost:8000.

Both code examples have a straightforward simplicity. The server setup is remarkably similar, though Express looks more compact here.

Working with Middleware

"Middleware" is the name given to software modules that work on HTTP requests in succession before the final output is returned to the user in a response.

Middleware can work as functions which we define inside the application, or functions defined in third-party middleware libraries.

Express lets you attach middleware to handle each request. Hapi, however, works with plugins that provide middleware functionality.

An example of middleware is CSRF tokens, which help prevent Cross-Site Request Forgery (CSRF) hacking attacks. Without CSRF tokens, attackers can impersonate legitimate requests and steal data or run malicious code on your applications.

The approaches taken by Hapi and Express to deal with CSRF attacks is similar. It involves generating a secret token on the server for each form that submits data back to the server. Then the server checks each POST request for the correct, difficult-to-forge token. If it is absent, the server rejects the request, thus avoiding malicious code execution attempts. In this case the middleware will generate a CSRF token for each request so you don't have to do it within your application code.

The difference between the two frameworks here is mainly cosmetic, with Hapi using a plugin, Crumb, for the token generation and processing. Express, on the other hand, uses a middleware known as csurf to generate and process CSRF tokens.

Middleware libraries can also help you with implementations of other common functionality such as authentication. You can find a list of some of the Express middleware at this middleware directory. For Hapi, there is a comparable library of Hapi plugins.

Start the server again and visit localhost:8000/cats, and you should see a list of cats displayed on the page.

To replicate this functionality in Express, first create a directory named views inside the expressapp root directory. Then create a Handlebars file cats.hbs with contents similar to what we had for Hapi.

Restart the Express server, and visit localhost:3000/cats, and we should see the list of cats displayed as it was for Hapi.

You will notice that the Hapi example needed a bit more configuration than the Express example. Both code examples are quite simple to follow, however. In a small app like this one, there is little to choose between the two frameworks. In a larger application, however, there will be more noticeable differences. For example, Hapi has a reputation for involving more boilerplate code than Express. On the other hand, it also does a bit more for you out of the box than Express, so there are definitely trade-offs.

Connecting to a Database

MongoDB is a battle-tested NoSQL database to use in your applications. With the mongoose object-modeling library, we can connect MongoDB to both Hapi and Express apps. For this part, you will want to have MongoDB installed and running on your system if you don't already have it.

To see how this works for both frameworks, let us now update our sample applications to store cat objects in a database. Likewise, our listings will fetch cat objects from the database.

Since we want the ability to create new cats from our /cats view, update the cats template to include a form for new cats. Add the following lines above the list of cats in hapiapp/templates/cats.html.

Now update our "GET" route for /cats to fetch cats dynamically from MongoDB. We will also add a new "POST" route where we can save cats to the database. Here is the code to make these changes, replacing the old GET /cats code:

Now when you run the server again and visit localhost:8000/cats, you will see that the cat list is now empty. But there is a form where you can enter new cat names and press the Enter key, and your cats will be saved to MongoDB. Enter a few cats and you get a list of cats displayed, as shown below:

For Express, installing and setting up Mongoose will look almost the same. Back in expressapp, install Mongoose with:

Now, restart the server and visit localhost:3000/cats. Just like for the Hapi app, we will now see an empty space with a form on top. We can enter cat names. The view will refresh, showing us a list of cats now saved to our MongoDB database.

This is one instance where the Express set up was a bit more complex than the equivalent for Hapi. However, notice that we didn't actually need to do too much with Hapi or Express to get the database connection set up - most of the work here was for the GET and POST routes.

Choosing Between Hapi and Express

As we saw, Hapi and Express have a lot in common. Both have an active user base, with Hapi being widely adopted by big enterprise teams. However, Express continues to outpace most other frameworks in adoption, especially for smaller teams.

Hapi makes sense if you have a well-defined app idea that fits within sensible defaults for the programming approach. Hapi takes a particular approach to things like app security, and its plugins system is not as extensive as the Express middleware ecosystem. If you are a large team, the conventions of Hapi can come in useful to keep your code maintainable.

Express, on the other hand, works best if you are after flexibility and making most development decisions on your own. For example, there might be ten different middleware that do the same thing in the Express ecosystem. It's up to you to consider from many options and make your choice. Express is best if you have an app you are not sure about the exact direction it will take. Whatever contingencies arise, the large Express ecosystem will provide the tools to make it happen.

Learn More

Want to learn more about web development in Node.js and actually create some Express.js apps? Here are a few courses that will give you some great experience:

The Web Developer Bootcamp

Node with React: Fullstack Web Development

Otherwise you can also get a great start by visiting each framework's respective websites:

Express Website

Hapi Website

Either way, the most important thing is you actually try them out, work through some examples, and decide on your own which is best for you.