During the Fullstack conference I saw a small project made with Hapi.js during a talk, so I decided to invest some time working with hapi in order to investigate how easy it was create a Node.js application with this framework.

I’ve to admit, this is a framework that is really well done. It has a plugin system that gives you a lot of flexibility when you are creating your server side application. It also has a decent community that provides a lot of useful information and plugins in order to speed up project development.

When I started to read the only book available on this framework I was impressed about the simplicity and the consideration behind the framework. More importantly I was impressed with where hapi was used for the first time. The first enterprise app made with this framework was released during Black Friday on Walmart e-commerce. The results were amazing! One of the main contributors of this open source framework is Walmart labs. It is a big organisation with real problems to solve; definitely a good starting point!

Express vs hapi

If you are asking why not Express, I can reply with a few arguments:

  • Express is a super lightweight and general purpose framework that works perfectly for small to medium size applications.
  • Hapi was built on top of Express at the beginning. They subsequently moved away in order to create something more solid, with more built-in functionalities. This is a framework that should speed up your productivity, and not give you a structure to follow.
  • Express is code-based whereas hapi is configuration-based (with a lot of flexibility of course)
  • Express uses middleware, hapi uses plugins
  • Hapi is built with testing and security in mind!

Hapi.js

Let’s start by saying that working with this framework is incredibly easy when you understand the concepts needed to create a Node project.

I created a sample project where I’ve integrated a Mongo database. There are a few end points exposed in order to add a new document inside a mongo collection, update a specific document, retrieve all documents available inside the database and retrieve all the details of a selected document.

Inside the git repo you can find also the front end code (books.html in the project root) in Vanilla Javascript, mainly because if you are passionate about React or Angular or any other front end library, you’ll be able to understand the integration without any particular framework knowledge.

What I’m going to describe now is how I’ve structured the server side code with hapi.

In order to create a server in hapi you just need few lines of code:

let server = new Hapi.Server();
server.connection();
server.start((err) => console.log('Server started at:', server.info.uri));

As you can see in the example (src/index.js) I’ve created the server in the first few lines after the require statements. I started the server (server.start) after the registration of the mongoDB plugin. But one step at a time.

Routes

After creating the server object, I’ve defined my routes with the server.route method. The route method will allow you to set just 1 route with an object or several routes creating an array of objects.

Each route should contain the method parameter where you’ll define the method to reach the path, you can also set a wildcard (*) so any method will be accepted in order to retrieve that path. You have to set the route path. Bear in mind you have to start always with slash (/) in order to correctly define the path. The path accepts also variables inside curly brackets as you can see in the last route of my example: path: ‘/bookdetails/{id}’.

Handler

Last but not least you need to define what’s going to happen when a client is requesting that particular path specifying the handler property. A handler expects a function with 2 parameters: request and reply.

This is a basic route implementation:

{
   method: 'GET',
   path: '/allbooks',
   handler: (request, reply) => { ... }
}

When you structure a real application, and not an example like this one, you can wrap the handler property inside the config property. Config accepts an object that will become your controller for that route.
So as you can see it’s really up to you pick up the right design solution for your project, it could be inline because it’s a small project. Alternatively it could be a PoC rather than an external module because you have a large project where you want to structure properly your code in a MVC fashion (we’ll see that in next blog post ;-)).
In my example I created the config property because you can then use an awesome library called JOI in order to validate the data received from the client application. Validating data with JOI is really simple:

validate: {
   payload: {
      title: Joi.string().required(),
      author: Joi.string().required(),
      pages: Joi.number().required(),
      category: Joi.string().required()
   }
}

In my example I checked if I receive the correct amount of arguments (required()) and in the right format (string() or number()).

MongoDB plugin

Now that we have understood how to create a simple server with hapi let’s go in deep on the hapi plugin system, the most important part of this framework. You can find several plugins created by the community. On the official website you can find also a tutorial that explainshow to create a custom plugin for hapi.js.

In my example I used the hapi-mongodb plugin that allows me to connect a mongo database with my node.js application. If you are more familiar with mongoose you can always use the mongoose plugin for Hapi.js.

One important thing to bear in mind of an Hapi.js plugin is that when it’s registered will be accessible from any handler method via request.server.plugins. It is injected automatically from the framework in order to facilitate the development flow.

Register the plugin

The first thing to do in order to use our mongodb plugin on our application is register it:

server.register({
   register: MongoDB,
   options: DBConfig.opts
}, (err) => {
   if (err) {
      console.error(err);
      throw err;
   }

   server.start((err) => console.log('Server started at:', server.info.uri));
});

As you can see I need just to specify which plugin I want to use in the register method and its configuration. This is an example of the configuration you need to specify in order to connect your MongoDB instance with the application:

module.exports = {
   opts: {
      "url": "mongodb://username:password@id.mongolab.com:port/collection-name",       
      "settings": {          
         "db": {             
            "native_parser": false         
         }
      }    
   }
}

In my case the configuration is an external object where I specified the mongo database URL and the settings.
If you want a quick and free solution to use mongoDB on the cloud I can suggest mongolab. When you register you’ll have 500mb of data for free per account, so for testing purposes it is really the perfect cloud service!
Last but not least, when the plugin registration happened I can start my server.

When I need to use your plugin inside any handler function I’ll be able to retrieve my plugin in this way:

var db = request.server.plugins['hapi-mongodb'].db;

Using the database

In my sample application, I was able to create few cases: add a new document (addbook route), retrieve all the books (allbooks route) and the details of a specific book (bookdetails route).

If you want to update a record in mongo, remember to use update method over insert method, because, if correctly handled, update method will check inside your database if there are any other occurrences and if there is one it will update that occurrence otherwise it will create a new document inside the mongo collection.

Below an extract of this technique, where you specify in the first object the key for searching an item, then the object to replace with. The last object you need to add is an object with upsert set to true (by default is false). That will allow you to create the new document if it doesn’t exist in your collection:

db.collection('books').updateOne({"title": request.payload.title}, dbDoc, {upsert: true}, (err, result) => {
    if(err) return reply(Boom.internal('Internal MongoDB error', err));
    return reply(result);
});

SAMPLE PROJECT GITHUB REPOSITORY

Resources

If you are interested in a more in depth exploration of hapi, I’d suggest looking at the official website or the book currently available.
And interesting news is that there are a other few books that will be published soon regarding Hapi.js:

That usually means Hapi js is getting adopted by several companies and developers. It is definitely a good sign for the health of the framework.

Hapi.js and MongoDB

| Modern Web| 538 views | 0 Comments
About The Author
- Solutions Architect at @dazn_de, Google Developer Expert (#GDE) and London JavaScript Community Manager @london_JS

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>