Shares

MongoDB and Hapi.js are two powerful technologies that allow working with REST API a cinch. You may already familiar with them, but let me do a quick review on it. 

Hapi.js is a very rich node.js framework for building applications and services. Hapi enables developers to focus on writing reusable application logic instead of spending time building infrastructure.

The most important point is the walmart team behind hapi.js has also been contributing to Node.js development in the past. So they pretty much understand how to control node power in their framework from the ground up.

MongoDB is an open-source document database that provides high performance, high availability, and automatic scaling.

A record in MongoDB is a document, which is a data structure composed of field and value pairs. MongoDB documents are similar to JSON objects. The values of fields may include other documents, arrays, and arrays of documents. These documents are stored in a collections. And collections are sub-part of DBs.

(Mongo) DB -> Collection -> Documents  == (Mysql) DB -> Tables -> Rows

Installing Hapi.js

First thing we need to do is install hapi.js.

NPM made it real simple to install node modules. Create a directory and from there:

Run: npm init  Follow the prompts and this will generate a package.json file for you.

Run: npm install –save hapi  This installs hapi, and saves it in your package.json as a dependency of your project.

That’s it! You now have everything you need in order to create a server using hapi.

Now we will create a basic server:

var Hapi = require('hapi');
var server = new Hapi.Server(3000);

server.start(function () {
    console.log('Server running at:', server.info.uri);
});

First, we require hapi. Then we create a new hapi server object, passing in a port number for the server to listen on. After that, start the server and you’ll see the prompt on console saying “Server running at: ….” Which means everything is up and running.

Now we will going to add few routes to our application for the sake of running it in browser.

Adding Routes:

var Hapi = require('hapi');
var server = new Hapi.Server(3000);

server.route({
    method: 'GET',
    path: '/',
    handler: function (request, reply) {
        reply('Hello, world!');
    }
});
server.route({
method: 'GET',
    path: '/test',
    handler: function (request, reply) {
        reply('Hello, test world!');
    }
});

server.route({
    method: 'GET',
    path: '/{name}',
    handler: function (request, reply) {
        reply('Hello, ' + encodeURIComponent(request.params.name) + '!');
    }
});

server.start(function () {
    console.log('Server running at:', server.info.uri);
});

Save the file and start the server again. You’ll get “Hello world” text as a response in browser if you try to visit http://localhost:3000“Hello, test world” for  http://localhost:3000/test and “Hello, haseeb” for http://localhost:3000/haseeb .If you want to explore more about the routes in hapi, check out this routing tutorialby hapi.js.Next we’ll jump into write REST API.

REST is basically a well defined way of interacting with web-servers. Resources are requested via URLs. RESTful services are about using various HTTP methods (GET, POST, PUT, DELETE) to manipulate data. Hapi.js can do it all with ease. We will write a simple API that allow us to get and save todo items. Initially our application will have an array consist of todo items. Each item will be an object.

var Hapi = require('hapi');
var todos = [
{
todo : “take a nap”,
note : “note for nap”
},
{
todo : “buy a book”,
note : “note for book”
},
{
todo : “read a blog”,
note : “note for blog”
},
];
var server = new Hapi.Server(3000);
Now let’s add a route “/todos” to fetch all of these todos.

server.route({
method: 'GET',
path: '/todos',
handler: function (request, reply) {
reply(todos);
}
});
Now what if want to get a todo item via id. We will add a route for getting an item via id parameter like “/todo/{id}”. This id can be any number less or equal to the total number of todo items.

server.route({
method: 'GET',
path: '/todo/{id}',
handler: function (request, reply) {
if (request.params.id) {
console.log(todos.length);
if (todos.length < request.params.id) {
return reply('No item found.').code(404);
}
return reply(todos[request.params.id-1]);
}
reply(todos);
}
});

You will get the item according to the id parameter. Save the file and start the server so you can test:http://localhost:3000/todo/1

http://localhost:3000/todo/2

http://localhost:3000/todo/3

Now we write a POST route for saving a todo item in our array that we get from POST request. we will add a validation object to ensure that all data is posted; otherwise, an error is returned. Joi is validation module which we use to validate our posted data.

server.route({
method: 'POST',
path: '/todos',
config: {

handler: function (request, reply) {
var newTodo = {
todo: request.payload.todo,
note: request.payload.note
};
todos.push(newTodo);
reply(todos);
           },

       validate: {
payload: {
todo: joi.string().required(),
note: joi.string().required()
}
}

}
});

”joi” here is a module that we require by:

var joi = require(“joi”);

Now we test our API for inserting the data, into our application using curl.

$ curl -X POST -H "Content-Type: application/json" -d '{"todo":"Build API", "note":"Hapi is is amazing"}' -i http://localhost:3000/todos

tada!!!

Our todo list are getting filled with new items.

We need to add ability to DELETE also.

server.route({
method: 'DELETE',
path: '/todo/{id}',
handler: function(request, reply) {
if (todos.length < request.params.id) {
return reply('No Todo found.').code(404);
}
todos.splice((request.params.id-1), 1);
reply(true);
}
});
Test it with following command:

$ curl -X DELETE http://localhost:3000/todo/2 -i

As you have noticed that we are passing “2” as id of the todo. It will delete 2nd todo from our todo list.

Enough with hapi and its routes. Now we need to add MongoDB in the game as data player. As of yet, we were using a local data object “todos” for storing data. And obviously this isn’t the real motive for storing data on the server. We want to keep it secure in the database. So let’s get it done too.

Installing MongoDB

First we need to download and install mongoDB. Brew is an appropriate tool to automate mongodb installation.

$ brew update
$ brew install mongodb

For more options to install mongodb, check the following link :http://docs.mongodb.org/manual/tutorial/install-mongodb-on-os-x/

On a successful installation, you’ll be able to access “mongod” ( primary daemon process for the MongoDB system) and “mongo” (CLI) from terminal or command prompt.

On “Mongo” CLI, there are is a lot you can do. Create and explore DBs, collections and data rows. Just like the mysql cli. Its amazing to operate it via command line. Play around with it a bit.

There is more help available on mongodb site to understand it.You should check this: http://docs.mongodb.org/manual/tutorial/getting-started/

For a small exercise, create a db name “mytodos” and within that db create a collection name “todos” and add few records in that collection.

All of this can easily done by mongo cli with executing few lines. Make sure you are inserting a document.

 Connecting with Hapi.js App

I am assuming that, you have installed and explored mongodb enough. So lets jump into making it breath with our hapi.js application. Let me explain you a bit about how mongodb actually get connect with hapi.

There is a node module “mongodb” created by mongodb Inc to assist developers to use it for connecting mongodb with their node applications. But since we are using hapi so we need another layer of framework supported implementation of mongdb module so make it real simple to use.

We’ll only be needing one node module that will do everything I have explained above.hapi-mongodb  https://www.npmjs.org/package/hapi-mongodb

Yeah right. We’ll going to use this simple node package in our application to deal with all the mongo operations.

Install it in your project directory.

$ npm install hapi-mongodb --save
We’re going to add few lines, required by hapi-mongodb as configurations for mongodb.

Put these lines after creating a server.

var dbOpts = {
"url"       : "mongodb://localhost:27017/mytodos",
"options"   : {
"db"    : {
"native_parser" : false
}
}
};

server.pack.require('hapi-mongodb', dbOpts, function(err){
if(err){
console.err(err);
throw err;
}

});

mongodb://localhost:27017/mytodos is the path to our db that we have recently created via mongo CLI.

We need make some changes in our route handlers to populate data with mongodb instead of that local object to store todos. I am going to all the routes again with new mongodb code.

server.route({
method: 'GET',
path: '/todos',
handler: function (request, reply) {
var db = request.server.plugins['hapi-mongodb'].db;
db.collection('todos').find().toArray(function (err, doc){
reply(doc);
});
}
});

server.route({
method: 'GET',
path: '/todo/{id}',
handler: function (request, reply) {
var db = request.server.plugins['hapi-mongodb'].db;
var ObjectID = request.server.plugins['hapi-mongodb'].ObjectID;

db.collection('todos').findOne({  "_id" : new ObjectID(request.params.id) }, function(err, result) {
if (err) return reply(Hapi.error.internal('Internal MongoDB error', err));
reply(result);
});
}
});

server.route({
method: 'POST',
path: '/todos',
config: {

handler: function (request, reply) {
var newTodo = {
todo: request.payload.todo,
note: request.payload.note
};
var db = request.server.plugins['hapi-mongodb'].db;
db.collection('todos').insert(newTodo, {w:1}, function (err, doc){
if (err){
return reply(Hapi.error.internal('Internal MongoDB error', err));
}else{
reply(doc);
}
});
},
validate: {
payload: {
todo: joi.string().required(),
note: joi.string().required()
}
}
}
});

server.route({
method: 'DELETE',
path: '/todo/{id}',
handler: function(request, reply) {
var db = request.server.plugins['hapi-mongodb'].db;
var ObjectID = request.server.plugins['hapi-mongodb'].ObjectID;

db.collection('todos').remove({"_id": new ObjectID(request.params.id)}, function (err){
if (err) return reply(Hapi.error.internal('Internal MongoDB error', err));
reply("Record Deleted");
});
}
});
You should add these routes and configuration code to your file and you are good to go. Try all the request we previously did with local todo object. All the requests will be handle smoothly by our new routes which involves database interactions. Let me add the curl commands for requests.
1. $ curl http://localhost:3000/todos"
2. $ curl http://localhost:3000/todo/5433…
3. $ curl -X POST -H "Content-Type: application/json" -d '{"todo":"Build API", "note":"Hapi is is amazing"}' -i http://localhost:3000/todos
4. $ curl -X DELETE http://localhost:3000/todo/5433... -i

So geeks this is it. We have a fully functional API that talks to our data pretty nicely. You can find all the source code Here. I hope this post helped you get familiarize with some great tools we have, for building awesome web apps. Thanks!

About Haseeb Khilji

Hi. I am Haseeb, a web enthusiast from Paksitan. I discovered the Web in 2003, and have been in love ever since. I am primarily a Node.js developer with a keen eye for startups. I'll guest post on Mr. Geek often to share my expertise on Web Development. Stalk me on social media to find out more.