what-is-HAPIness

Daniela Borges

Daniela Borges

hapijs/bossy lead maintainer, mentor

photography amateur

origami master

full-stack dev

ylder

@sericaia

Walmart

what-is-hapi

Which problems hapi tried to solve?

...there are already solutions in the market

what-is-hapi

A rich framework for building applications and services

HTTP API server

what-is-hapi   //main goals

everything is a PLUGIN in hapi

focus on writing reusable

application logic

what-is-hapi  //main goals

1. configuration is better than code

 

2. business logic must be isolated from transport layer 

>>> delivering business value

what-is-hapi   //main goals

1. configuration  is better than code

 

 

    var Hapi = require('hapi');
     
    var config = {
       dbConnection: 'mongodb://myvoodoapp',
       drinks: ['capirinha', 'mojito', 'margarita']       
    };
     
    var server = new Hapi.Server({
      app: config
    });
    
    server.start();

what-is-hapi   //main goals

1. configuration  is better than code

 

 

    var Hapi = require('hapi');
     
    var config = {
      dbConnection: 'mongodb://myvoodoapp',
      drinks: ['capirinha', 'mojito', 'margarita']    
    };
     
    var server = new Hapi.Server({
      app: config
    });
    
    server.start();

accessible through

server.settings.app 

what-is-hapi   //main goals

2. business logic must be isolated from transport layer 

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

    server.connection({ 
        host: 'localhost', 
        port: 8000 
    });
    
    server.route({
        method: 'GET',
        path:'/api/mediterraneajs', 
        handler: function (request, reply) {
           reply('Mediterranea JS...the best route to meet Barcelona!');
        }
    });

    server.start();

index.js 

what-is-hapi   //main goals

2. business logic must be isolated from transport layer 

what-is-hapi   //main goals

even better

...using PLUGINS

what-is-hapi   //main goals

even better...using plugins! 

    var register = function (plugin, options, next) {
    
        plugin.route({
            method: 'GET',
            path:'/api/mediterraneajs', 
            handler: function (request, reply) {
               reply('Mediterranea JS...the best route to meet Barcelona!');
            }
        });

    
        next();
    };
    
    register.attributes = {
      name : 'mediterraneaApi',
      version : '1.0.0' // I am confident!
    }
    
    module.exports = register;

mediterraneaApi.js 

what-is-hapi   //main goals

even better...using plugins! 


    var Hapi = require('hapi');
    var server = new Hapi.Server();
    server.connection(...);
    
    
    // Register the API
    server.register( [ mediterraneaApi, (...) ] ,
                      function (err) {});
    
    server.start();

index.js 

what-is-hapi   //main goals

small things matter.. use prefix, please! I am a lazy dev

source: http://projectzomboid.com/blog/wp-content/uploads/2014/08/BC_Keep_Calm_and_Stay_Lazy_2_copy-712x462.jpg

what-is-hapi   //main goals

small things matter.. c'mon, use prefix!

    var mediterraneaApi = require('./mediterraneaApi.js');

    server.register({register: mediterraneaApi}, {
        routes: {
            prefix: '/api'
        }
    }, function (err) {
    });

what-is-hapi   //request lifecycle

{ proposal: understand request object and request lifecycle }

what-is-hapi   //request lifecycle

{ Request object }

The request object is created internally for each incoming request.

 

​The request object methods and properties change throughout the request lifecycle.

// hapi docs

what-is-hapi   //request lifecycle

Why I may need this?

  • route interception
  • validation
  • authentication
  • encryption (ex. create SHA1 hash of each request body)
  • ...

what-is-hapi   //request lifecycle

Extension Points

what-is-hapi   //demo

we can modify each extension point using 

#1 FACT

server.ext(extension_point_event, method, [options])

what-is-hapi   //request lifecycle

'onRequest' extension point


    server.ext('onRequest', function (request, reply) {
    
        request.setUrl('/vodoo');
        return reply.continue();
    });

what-is-hapi   //request lifecycle

'onRequest' extension point

    server.ext('onRequest', function (request, reply) {
    
        request.setUrl('/vodoo');
        return reply.continue();
    });

    server.route({ 
        method: 'GET', 
        path: '/vodoo', 
        handler: function (request, reply) {
            return reply("making vodoo interceptor @MediterraneaJS");
        }
    });

    server.start();

what-is-hapi   //request lifecycle

Extension Points

what-is-hapi   //request lifecycle

what-is-hapi   //request lifecycle

Let's divide to conquer...

what-is-hapi   //request lifecycle

always called

request.setUrl() and request.setMethod() methods

 

'onRequest'

what-is-hapi   //request lifecycle

Authentication

what-is-hapi   //request lifecycle

verify route permissions

   - may be specified by a strategy

 

set and authenticate payload

what-is-hapi   //request lifecycle

server.auth.strategy(name, scheme)

 identify our specific strategy

 general type of auth

what-is-hapi   //request lifecycle

    var HapiAuthBasic = require('hapi-auth-basic');
    
    //...
    
    var validate = function (username, password, callback) {
    //...
    };
    
    server.register(HapiAuthBasic, function (err) {
        server.auth.strategy('simple', 'basic', { validateFunc: validate });

        server.route({
            method: 'GET',
            path: '/',
            config: {
                auth: 'simple',
                handler: function (request, reply) {
                    reply('hello, ' + request.auth.credentials.user);
                }
            }
        });
    });

uses hapi-auth-basic

defines 'basic' scheme

what-is-hapi   //request lifecycle

    var HapiAuthBasic = require('hapi-auth-basic');
    
    //...
    
    var validate = function (username, password, callback) {
    //...
    };
    
    server.register(HapiAuthBasic, function (err) {
        server.auth.strategy('simple', 'basic', { validateFunc: validate });
        
        server.route({
            method: 'GET',
            path: '/',
            config: {
                auth: 'simple',
                handler: function (request, reply) {
                    reply('Welcome, ' + request.auth.credentials.user);
                }
            }
        });
    });

defines the strategy

what-is-hapi   //request lifecycle

    var HapiAuthBasic = require('hapi-auth-basic');
    
    //...
    
    var validate = function (username, password, callback) {
    //...
    };
    
    server.register(HapiAuthBasic, function (err) {
        server.auth.strategy('simple', 'basic', { validateFunc: validate });

        server.route({
            method: 'GET',
            path: '/',
            config: {
                auth: 'simple',
                handler: function (request, reply) {
                    reply('hello, ' + request.auth.credentials.user);
                }
            }
        });
    });

sets auth = our strategy

what-is-hapi   //request lifecycle

server.auth.scheme('basic', scheme);
server.auth.strategy('vodoo', 'basic');
server.auth.default('vodoo');

what-is-hapi   //request lifecycle

More about authentication

hapi-auth-cookie 

     uses 'cookie' schema

 

bell

    3rd party

    uses 'bell' schema

 


    server.auth.strategy('twitter', 'bell', {
        provider: 'twitter',
        password: 'encryption_password',
        clientId: 'client_id',
        clientSecret: 'client_secret'
    });

what-is-hapi   //request lifecycle

Validation

what-is-hapi   //request lifecycle

    plugin.route({
        method: 'GET',
        path:'/welcome/{name}',
        handler: function (request, reply) {

            var welcomeMsg = 'Welcome ' + request.params.name + "!\n";
            welcomeMsg += 'Are you ' + request.query.mood + "?";

            reply(welcomeMsg);
        },
        config: {
            validate: {
                params: {
                    name: Joi.string().required()
                },
                // ...
            }
        }
    });

Validate params

what-is-hapi   //request lifecycle

    plugin.route({
        method: 'GET',
        path:'/welcome/{name}',
        handler: function (request, reply) {

            var welcomeMsg = 'Welcome ' + request.params.name + "!\n";
            welcomeMsg += 'Are you ' + request.query.mood + "?";

            reply(welcomeMsg);
        },
        config: {
            validate: {
                // ...
                query: {
                    mood: Joi.string().valid(["happy","sad"])
                             .default("happy")
                }
            }
        }
    });

Validate query

what-is-hapi   //request lifecycle

May contain an object or array of objects with actions

Actions 'terminate' with a reply(), and pass the result to the next step

Route prerequesites

what-is-hapi   //request lifecycle


var caipirinhaIngredients = function (request, reply) {
    return reply('Cachaça, lime, and sugar');
};

var mojitoIngredients = function (request, reply) {
    return reply('Mint leaves, limes, rum, sugar, and soda');
};

var partyDrinks = function (request, reply) {
    return reply(request.pre.caipirinha + ' and ' + request.pre.mojito);
};

Route prerequesites

what-is-hapi   //request lifecycle


    server.route({
        method: 'GET',
        path: '/party',
        config: {
            pre: [
                [
                    /* capirinha and mojito executed in parallel */
                    { method: caipirinhaIngredients, assign: 'caipirinha' },
                    { method: mojitoIngredients, assign: 'mojito' }
                ],
                { method: partyDrinks, assign: 'drinks' },
            ],
            handler: function (request, reply) {
                return reply(request.pre.drinks);
            }
        }
    });

Route prerequesites

what-is-hapi   //request lifecycle

between onPreHandler and onPostHandler we can change reply(), i.e we can change the response

#2 FACT

what-is-hapi   //request lifecycle

uses the signature function(request, reply)

 

Route handler

request object

reply interface

what-is-hapi   //request lifecycle

The response object contained in request.response may be modified

'onPostHandler'

what-is-hapi   //request lifecycle

always called

The response object contained in request.response may be modified

'onPreResponse'

what-is-hapi   //request lifecycle

may emit 'request-error' event

 

finally returns data to client!

Send response

what-is-hapi   //request lifecycle

request.tail([name]) 

     Returns a tail function which must be called when the tail activity is completed

 

server emits one 'tail' event when all tails are completed

tails

what-is-hapi   //request lifecycle

Extension Points/Conclusion

hapi-auth-basic/cookie/bell

joi

*

*

* always called

what-is-hapi   //latest news

What's next in hapi?

what-is-hapi   //latest news

micro-services plugin

Chairo

Hapi Mentorship program

Call for lead maintainers

what-is-hapi   //latest news

Q & A

Daniela Borges

hapijs/bossy lead maintainer, mentor

photography amateur

origami master

full-stack dev

ylder

@sericaia

Made with Slides.com