HAPI


jaxnode july 2014

Code on the Beach August
RobotsCONF December

Hapi HAPI, joi joi


http://youtu.be/y0V4TZAyd8I


hapi.js

Hapi short for HTTP API, or so they say
Developed by Walmart Labs
Eran Hammer also authored OAuth
Developed as a proxy for Mobile Services


walmart really?

Walmart is very high tech
Invented EDI
Replacing legacy AS400 and Java services with Node.js
NPM rewritten from scratch with Hapi



req, res, next

Express, Connect and Director follow pattern
Middleware Hell
Modules needed for all features you need
Express to be replaced by Koa
Koa requires ES6

Hapi.js

Hapi full framework for Rest and Web
Open Source
@ Version 6
Routes
Caching
Cookies
Sessions
Logging
Authentication
Plugins
100% code coverage


Configuration over code

You can configure with code
Configurations can be stored in json and loaded at runtime

http Object Abstraction

Request and Response Object can change
HTTP objects have changed with versions of Node
They have changed with Express
Express gives you raw request objects
Hapi provides those objects if you need them

Three R's of hapi

Reflection - Code stubing and documentation
Reduce Errors - Validation and Route handling
Reusability - business logic, helpers, caching

Plugins

Hapi framework will slowly add features into Hapi, and then extract out plugins if warranted
Joi validation is plugin
Swagger, Lout plugin for Documentation
var Hapi = require('hapi');

            // Create a server with a host and port
            var server = new Hapi.Server('localhost', 8000);

            // Add the route
            server.route({
                method: 'GET',
                path: '/hello',
                handler: function (request, reply) {

                    reply('hello world');
                }
            });

            // Start the server
            server.start();
            
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: '/{name}',
                handler: function (request, reply) {
                    reply('Hello, ' + encodeURIComponent(request.params.name) + '!');
                }
            });

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

handler

2.0 adding breaking change
fn(request, reply);
function myHandler(request, reply) {
            reply(msg + "Hello, World");
            }
request contains raw references to both the request and response objects

using plugins

var Good = require('good');

            server.pack.register(Good, function (err) {
                if (err) {
                    throw err; // something bad happened loading the plugin
                }

                server.start(function () {
                    server.log('info', 'Server running at: ' + server.info.uri);
                });
            });
            
Plugins are really used to modularize your app
Catalog and Cart could be two different plugins

Routes

server.route({
                method: 'GET',
                path: '/',
                handler: someHandler
            });

            server.tables();

packs

Packs are a way for Hapi to combine multiple servers into a single unit, and are designed to give a unified interface when working with plugins.
You can assign labels to each server.
You can have 'PublicWeb', 'Api' and 'Admin' as separate packs.

plugins

exports.register = function (plugin, options, next) {
                next();
            };

            exports.register.attributes = {
                pkg: require('./package.json')
            };
            
or
var myPlugin = {
                register: function (plugin, options, next) {
                    next();
                }
            }

            myPlugin.register.attributes = {
                name: 'myPlugin',
                version: '1.0.0'
            };    
            

validation

Hapi uses Joi validation
Failed validation returns 400 status code
server.route({
                method: 'GET',
                path: '/hello/{name}',
                handler: function (request, reply) {
                    reply('Hello ' + request.params.name + '!');
                },
                config: {
                    validate: {
                        params: {
                            name: Joi.string().min(3).max(10)
                        }
                    }
                }
            });
            

HELPERS, er I mean server methods

var add = function (a, b, next) {

                next(null, a + b);
            };

            server.method(
            { name: 'sum', fn: add, options: { cache: { expiresIn: 2000 } } });

            server.methods.sum(4, 5, function (err, result) {
                console.log(result);
            });
            

CACHing

Uses the Catbox module
Support for in memory cache up to 100MB
Support for scalable cache through Redis, MongoDB, Memcached, and Riak
cache: {
                expiresIn: 60 * 1000,
                staleIn: 45 * 1000,
                staleTimeout: 500
            }    
            

Routing config options

Can specify handlers
Set up endpoint cache
You can also setup Route prerequisites for handling asynchronous and synchronous processing before replying to an http request
server.route({
                method: 'GET',
                path: '/hello/{name}',
                config: {
                    handler: function (request, reply) {
                        reply('Hello ' + request.params.name + '!');
                },
                validate: {
                    params: {
                        name: Joi.string().min(3).max(10)
                    }
                }
                }
            });

var pre1 = function (request, reply) {
                reply('Hello');
            };
            var pre2 = function (request, reply) {
                reply('World');
            };
            var pre3 = function (request, reply) {
                reply(request.pre.m1 + ' ' + request.pre.m2);
            };
            server.route(...
                config: {
                    pre: [
                        [
                            // m1 and m2 executed in parallel
                            { method: pre1, assign: 'm1' },
                            { method: pre2, assign: 'm2' }
                        ],
                        { method: pre3, assign: 'm3' },
                    ],
                    handler: function (request, reply) {
                        reply(request.pre.m3 + '\n');
                    }
                }
            });
            

Views

Hapi will support all of the major template engines
var server = new Hapi.Server({
                views: {
                    engines: { html: require('handlebars') },
                    path: __dirname + '/templates'
                }
            });

            var handler = function (request, reply) {

                var context = {
                    title: 'Views Example',
                    message: 'Hello, World'
                };

                reply.view('hello', context);
            };

Static content

Hapi gives the developer precise control over hosting 
static content
{ 
                method: 'GET',
                path: '/images/{path*}, 
                handler: { 
                    directory: './public/images',
                    listing: false
                } 
            }
            

server options

Hapi has many configurable options
Built in support for CORS
Can configure cache at server level
Well documented

Cookies

Hapi.state object used for configuring cookies
var handler = function (request, reply) {
                var maxCookieSize = 512;
                var cookieOptions = {
                    encoding: 'iron',
                    password: 'secret'
                };
                var content = request.pre.user;
                Hapi.state.prepareValue('user', content, cookieOptions, function (err, value) {
                    if (err) {
                        return reply(err);
                    }
                    if (value.length < maxCookieSize) {
                        reply.state('user', value, { encoding: 'none' } );
                    }
                    reply('success');
                });
            };
            

DEMo time

Questions

Resources

http://hapijs.com/ provides great documentation
Git repo found at https://github.com/spumko/hapi/
My Git repo for tonights Demo: 
https://github.com/davidfekke/jaxnodeHapi
Eran Hammer's blog: http://hueniverse.com/

contact

David Fekke at gmail dot com
Twitter: @davidfekke