In the first post of the HapiJS series we learned how to use the server interface to build a server. Today let’s make an API we can use by adding routes and request handlers. Create a new file like so:

File: server.js
var Hapi = require('hapi');

var server = new Hapi.Server();

server.connection({ port: 9000 });

server.start(function () {
    console.log('API up and running at %s', server.info.uri);
});

Because HapiJS is designed for configuration over code, adding routes is just a matter of passing a JavaScript object to the server#route function.

File: server.js
server.route({
    path: '/lotto-numbers/{unixDate}',
    method: 'GET',
    handler: function (request, reply) {
        console.log('headers as obj', request.headers);
        console.log('query params as obj', request.query);
        console.log('client IP is %s', request.info.remoteAddress);
        console.log('path param unixDate is %s', request.params.unixDate);
    
        reply({ numbers: '1 12 36 22 50 2' });
    }
});

You can follow how easy it is to add a route:

  1. Providg a URI for the path
  2. Specify the HTTP method to respond to
  3. Write a handler function for the incoming HTTP request

The first parameter in the handler function is Hapi’s request object. It encapsulates the data you would expect to have access to like headers, query parameters, and path parameters but includes other goodies that are part of the Hapi ecosystem too. I suggest looking at the documentation to see what else is available to you.

File: server.js
server.route({
    path: '/bankaccount/{id}/deposit',
    method: 'POST',
    handler: function (request, reply) {
        // A JSON request body might look like { "amount": 100000000 }
        var amount = request.payload,
            accountId = request.params.id;
        
        var deposited = BankAccount.deposit(accountId, amount);
        if (deposited) {
            reply({ status: 'deposit complete').status(201);
        }
        else {
            reply({ status: 'deposit incomplete' }).status(500);
        }
    }
});

Building a REST API in NodeJS is simple with a framework specifically made for the task. Eran Hammer and the team at Walmart Labs created HapiJS to connect their mobile platforms with a legacy Java backend. Sounds like complex integrations coupled with with large scale maintainability requirements to me!

I use Hapi to build my platform APIs for that very reason. I’ll demonstrate how simple it is to construct a good foundaton for your REST API.

Today let’s focus on the first step of creating a server. We’ll go over the basics and touch on some handy features.

Installing Hapi

First make sure you have a recent version of NodeJS installed. To get started let’s create a new Node project with npm init`. Just hit enter to accept all the defaults if you want.

npm init
npm install --save hapi nodemon
touch server.js

The --save flag will write the module dependencies to your package.json file. For local development I use nodemon to run my API. It watches the project folder for changes and restarts automatically.

Creating a server

Open the server.js file and type in the following code:

File: server.js
 1 var port = process.env.PORT || 9000;
 2 
 3 var Hapi = require('hapi');
 4 
 5 var server = new Hapi.Server();
 6 
 7 server.connection({ port: port });
 8 
 9 server.start(function () {
10     console.log('Server started at port %d', port);
11     console.log('Resistance is futile');
12 });

No ceremony here! If you want you can provide a port number to start your API at. Otherwise it will default to port 9000.

PORT=9001 nodemon server.js

Application settings

Real world projects usually have some kind of configuration file with environment specific values.

File: config.js
module.exports = {
    "development": {
        "someProvider": "http://dev.some.provider.com"
    },
    "qa": {
        "someProvider": "http://qa.some.provider.com"
    }
};

Instead of having require('./config.js') wherever you need configuration values, you can DRY up your code by giving your configuration values to the server when you call the constructor function.

File: server.js
 1 var port = process.env.PORT || 9000;
 2 var env = process.env.NODE_ENV || 'development';
 3 
 4 var Hapi = require('hapi');
 5 
 6 var server = new Hapi.Server({ app: require('./config')[env] });
 7 
 8 server.connection({ port: port });
 9 
10 server.start(function () {
11     console.log('app setings', server.app);
12 });

Multiple servers

Sometimes it’s useful to partition your app into different servers. Maybe you want the admin part of your API to be completely separate from your core services. A lot of times I need to run a proxy to legacy APIs too.

File: server.js
 1 var port = process.env.PORT || 9000;
 2 var env = process.env.NODE_ENV || 'development';
 3 
 4 var Hapi = require('hapi');
 5 
 6 var server = new Hapi.Server({ app: require('./config')[env] });
 7 
 8 server.connection({ port: 9000, labels: ['core'] });
 9 
10 server.connection({ port: 9001, labels: ['admin'] });
11 
12 server.connection({ port: 9002, labels: ['proxy'] });
13 
14 server.start(function () {
15     console.log('core', server.select('core').info);
16     console.log('admin', server.select('admin').info);
17     console.log('proxy', server.select('proxy').info);
18 });

That’s a wrap on some Hapi server methods I find useful. Next time we’ll learn how to configure routes and add request handlers.

So it’s time I start blogging (again). I wrote earlier in my career and stopped caring. I really wish I hadn’t because I’d have 10 years worth of content right now.

John Sonmez just released Soft Skills and I skipped to the section about marketing myself as a developer. I’ve been working on personal branding for a year now by presenting at local meetups and organzing AngularJS-OC. John’s arguments for blogging really hit home though so that’s why I’m here.

Let’s not have anything to meaty yet for a first post though. A technical blog needs some code so here is something ultra simple to get us started.

var Hapi = require('hapi');

var server = new Hapi.Server();

server.connection({ port: 8080 });

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

server.start(function () {
    console.log('Api is up and running at ' + server.info.uri);
});