Shreyansh Pandey
This tutorial is out of date and no longer maintained.
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.
Everyone hates a tutorial that 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 approach”), I have broken this tutorial in 2 parts. In sequence, the following will be the outline:
Note: The second part is up at Making a RESTful API with Hapi.js.
After deployment, we will have a fully functional and consumable API with all the goodies. So, what are we waiting for? Let’s get started.
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 performant, maintainable code.
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
every time 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.
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 in the root directory:
- npm init
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 favorite text editor in the root directory. Your directory structure should look something like the following:
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 in the root directory:
- npm install --save babel-core babel-preset-es2015 hapi
And wait for the dependencies to finish installing.
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 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 equivalent.
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.
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?
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 in the command line:
- npm start
And your output should resemble something like below:
- getting-started-with-hapi-js-part-1@1.0.0 start /path/to/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 outlined here.
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;Hello World
serverAlright, 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 from the command line:
- npm start
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 that 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:
Now, run npm start
; the console will say something like
Server started at http://example.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:
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.
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.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!