Getting Started with Hapi.js

Free Course

Build Your First Node.js Website

Node is a powerful tool to get JavaScript on the server. Use Node to build a great website.

Introduction

Separation of Concerns is the in thing these days, and with the introduction of RESTful architecture, it has been a cakewalk; however, new developers often face the dilemma of choosing their weapons. The frameworks, and the suite of tools they will use to develop their next killer application.

Not long ago, I was one of them, and then I came across this excellent talk by Eran Hammer from WalmartLabs, and decided that Hapi.js was the way to go. The problem with Hapi.js is that it is simple and complicated at the same time. Honestly, I find it cleaner and easier than Express.js, and you will see why I say that once we're done with this segment.

Our goal

Everyone hates a tutorial which just contains some incoherent code, and expects you to follow along. And hence, we will actually make something; by the end of this series, we will have a fully functional application to add pictures of cute cats. (Really, this is the kind of application I want to work on.)

Since covering everything in one long post will be anything but smart (and because we love a "modular apporach"), I have broken this tutorial in 2 parts. In sequence, following will be the outline:

  • Part 1 - a gentle introduction to Hapi.js; we set up the environment and all the dependencies; simple Hello World;
  • Part 2 - creating a fully blown RESTful API using Hapi.js

After deployment, we will have a fully functional and consumable API with all the goodies. So, what are we waiting for for? Let's get started.

What is Hapi.js?

hapi.js Website

Hapi (pronounced "happy") is a web framework for building web applications, APIs and services. It's extremely simple to get started with, and extremely powerful at the same time. The problem arises when you have to write perfomant, maintable code.

Let's Start

With that said, let's dive straight in. But before we get to coding, let me explain what our development environment will look like.

I am a fan of ES6, and really, I can't write code in the old ES5 syntax. I will show you a really quick way to get started with ES6 without the transpiling part. That's great: it means that you don't have to run gulp build:development everytime you make a change.

We'll use Babel for on-the-fly transpilation of the ES6 code. I say on the fly simply because there is no direct output; if you're familiar with CoffeeScript's require hook, it'll be just like that. If you are not, then don't worry because this is really, really simple.

Directory Structure

For starting out, we'll have a fairly simple directory structure with just one src folder, and all of the program files will reside in that folder. In the root folder, we'll have all other build toolchain files.

Start by finding the directory of your choice, and point your terminal to it. I love the command line, so I do:

mkdir getting-started-with-hapi-js-part-1 && cd $_
mkdir src
touch .babelrc .gitignore .jshintrc src/server.js

Notice the $_. This means, use the last argument of the last command. So, this will turn to cd getting-started-with-hapi-js-part-1 An interesting shorthand.

Let's examine the dotfiles I have created:

  • .gitignore -- tell Git what all to ignore;
  • .babelrc -- tell the BabelJS transpiler which presets and/or plugins to use;
  • .jshintrc -- tell our linter (JSHint) what all we expect from it.

With that done, execute npm init in the root directory. Fill in all the details; it should look something like the following:

{
    "name": "getting-started-with-hapi-js-part-1",
    "version": "1.0.0",
    "description": "Getting started with Hapi.js -- Part 1",
    "main": "index.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "repository": {
        "type": "git",
        "url": "git+https://github.com/labsvisual/getting-started-with-hapi-js-part-1.git"
    },
    "author": "Shreyansh Pandey",
    "license": "MIT",
    "bugs": {
        "url": "https://github.com/labsvisual/getting-started-with-hapi-js-part-1/issues"
    },
    "homepage": "https://github.com/labsvisual/getting-started-with-hapi-js-part-1#readme"
}

Oh, before I forget, all of the code is available at the GitHub repository.

Now, open your favourite text editor in the root directory. Your directory structure should look something like the following:

initial-directory-structure

Perfect. Now, let's install the dependencies. We'll go with the real basics which are required to spin up a simple Hello World along with some decoration to that. Right now, we'll just install babel-core, babel-preset-es2015, and hapi.

For those that don't know, Babel is a transpiler that helps us convert ES6 JS code to ES5 code.

Run npm install --save babel-core babel-preset-es2015 hapi in the root directory, and wait for the dependencies to finish installing.

Till then, let's prepare our .babelrc, .gitignore, and our .jshintrc files. The configuration here is really simple, but you can always tweak it to your requirement.

In the .babelrc file, we need to tell Babel we that are using the es-2015 preset. A preset is a set of transformations which something should go through so that it's converted to something else. Here, for example, the es-2015 preset includes all the transforms to convert the fancy ES6 functions to their respective ES5 equivalant.

Add the following to the .babelrc file:

{
    "presets": [ "es2015" ]
}

Yeah, really! That's it.

In the .gitignore file, we want to exclude the node_modules folder, so adding node_modules to that file will do the job.

Lastly, .jshintrc needs to know just one thing: we're using ES2015. For that, add the following snippet to the file:

{
    "esnext": true
}

And that's it. We're done with our initial configuration. Now let's write some code.

Bootstrapping for ES6

Lastly, we need to create a bootstrap.js file which will require the babel-core's registering module, and the main module from our code: in this case server.js. And, we'll also create a very simple shortcut in the package.json file so that we can just run npm start to fire up our Hapi.js project.

Start by creating a bootstrap.js file in the root folder, and add the following two lines:

require( 'babel-core/register' );
require( './src/server' );

The first line calls for the babel register module, which then loads the server module. This helps Babel in transpiling on the fly. Simple, eh?

Getting Lazy with package.json

Node has a concept of scripts; you can define them in the scripts section of the package.json file. Modify the existing scripts section to look something like this:

...
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node bootstrap.js"
},
...

Now, run npm start in the command line, and your output should resemble something like below:

> getting-started-with-hapi-js-part-1@1.0.0 start /Users/labsvisual/Documents/Scotchio-Articles/getting-started-with-hapi-js-part-1
> node bootstrap.js

Nothing happened? Well, that's because we don't have any code, don't worry. However, if there is some error, be sure to check and see if you have followed all the steps outlines here.

Introduction to the Terms

Before we get Hapi-specific, let's have a look at the terms the framework throws at us. Don't worry, there are only three main terms.

  • server -- the root object which contains everything about the web application;
  • connection -- an instance of a connection, usually a host and a port where the requests will come to;
  • route -- a URI within a connection telling the server which function to execute when;

Create a Simple Hello World server

Alright, now we come to the best part: creating the actual server. In this part, we'll only create a few example routes to see what all Hapi has to offer; we'll modify this in the next parts and soon, it'll start looking like our application.

So, start by importing Hapi into the server.js file:

import Hapi from 'hapi';

Perfect. Now, we'll create a new server instance, and attach a new connection to it. Add the following bit to create a new server instance:

const server = new Hapi.Server();

server.connection( {
    port: 8080
});

Now, I have skipped the host field because that's a known bug. I can't find a link right now, but I will update the post once I find the official link to the bug.

Cool! Now, run your server by typing npm start from the command line.

And yet again, there is no output. That's because we haven't yet started the server, nor have we defined a route. Let's define a simple hello route:

server.route({

    method: 'GET',
    path: '/hello',
    handler: ( request, reply ) => {
        reply( 'Hello World!' );
    }

});

The block here is self-explanatory, apart from maybe the handler. The handler is the function which is executed when the specific path is hit. So, in this case, the anonymous function will be executed if the user visits the path /hello.

The request parameter represents the entire request: it has the query strings, the URL parameters, and the payload (if it's a POST/PUT request; we'll see this later).

The reply object helps in sending a reply back to the client. Here, we're just sending back a simple Hello World! reply. Nothing special.

Lastly, let's write the code to start the server.

server.start(err => {

    if (err) {

        // Fancy error handling here
        console.error( 'Error was handled!' );
        console.error( err );

    }

    console.log( `Server started at ${ server.info.uri }` );

});

The server.info property contains an object which contains the following information:

  • created - the time the server instance was created;
  • started - the time the server instance was started;
  • host - the hostname of the machine;
  • port - the port to which the server the server is listening ;
  • protocol - the protocol on which the server is operating;
  • id - a unique ID of the server instance;
  • uri - the complete URI of the current server instance;
  • address - the address the server is bound to

Now, run npm start; the console will say something like

Server started at http://Shreyanshs-MacBook-Pro.local:8080

If that's the case, then point your browser to localhost:8080/hello and hit enter. You'll see a page something like the following:

localhost-hello-world

If you see errors like the following:

Error was handled!
{[Error: listen EADDRINUSE 0.0.0.0:8080]
  code: 'EADDRINUSE',
  errno: 'EADDRINUSE',
  syscall: 'listen',
  address: '0.0.0.0',
  port: 8080 }

It means that some application is using port 8080, try a different port and your application should work flawlessly.

You can play around with it, but when you are done, hit Ctrl + C to exit out of the server.

Conclusion

I guess that got your feet wet, and you are excited to try out this exciting framework. In the upcoming tutorials, we'll have a look at all the goodies in the request object; try out different request verbs, and use Paw to test our URLs.

If you get stuck, be sure to check the API documentation; they have some really good explanations there.