Seneca micro-services plugin
JavaScript HTML
Latest commit df6287e Nov 23, 2016 @geek geek committed on GitHub Merge pull request #59 from geek/master
Version 3
Permalink
Failed to load latest commit information.
lib Update to Seneca v3 Nov 21, 2016
test Update to Seneca v3 Nov 21, 2016
.gitignore initial commit May 20, 2015
.travis.yml Update deps Nov 22, 2016
CONTRIBUTING.md initial commit May 20, 2015
LICENSE initial commit May 20, 2015
README.md Update to Seneca v3 Nov 21, 2016
package.json version 3.0.0 Nov 22, 2016

README.md

chairo

npm version Build Status

chairo ("happy" in ancient Greek: χαίρω) is a Seneca micro-services plugin for hapi. The plugin integrates the Seneca functionality into hapi and provide tools to map its actions to server methods and views for easy access.

Lead Maintainer - Wyatt Preul

Usage

Plugin Registration

chairo is registered with a hapi server using the server.register() method. Once registered it decorates the server object with a reference to the seneca object initialized using the provided plugin options. Default plugin options:

  • seneca: false
  • log: silent
  • actcache:
    • active: false

You can use an existing instance to seneca and let chairo do the decorations for your server. When setting the seneca option on the configuration, please note that the other settings won't be used.

const Chairo = require('chairo');
const Hapi = require('hapi');

const server = new Hapi.Server();
server.connection();

// Register plugin

server.register({ register: Chairo }, function (err) {

    // Add a Seneca action

    let id = 0;
    server.seneca.add({ generate: 'id' }, function (message, next) {

        return next(null, { id: ++id });
    });

    // Invoke a Seneca action

    server.seneca.act({ generate: 'id' }, function (err, result) {

        // result: { id: 1 }
    });
});

In addition, the hapi request object is decorated with a reference to the seneca object for easy access:

server.route({
    method: 'POST',
    path: '/id',
    handler: function (request, reply) {

        // Invoke a Seneca action using the request decoration

        request.seneca.act({ generate: 'id' }, function (err, result) {

            if (err) {
                return reply(err);
            }

            return reply(result);
        });
    }
});

server.action(name, pattern, [options])

Maps a Seneca action pattern to a hapi server method where:

  • name - the server method name (same as the name used in server.method()).
  • pattern - the Seneca action pattern (e.g. 'generate:id' or { generate: 'id' }) to map.
  • options - optional settings options where:
    • cache - method caching options (same as the name used in server.method()).
    • generateKey - method generating custom cache key (same as the name used in server.method()).
const Chairo = require('chairo');
const Hapi = require('hapi');

const server = new Hapi.Server();
server.connection();
server.register(Chairo, function (err) {

    // Set up a Seneca action

    var id = 0;
    server.seneca.add({ generate: 'id' }, function (message, next) {

        return next(null, { id: ++id });
    });

    server.seneca.add({ calc: 'average' }, function (message, next) {

        return next(null, { average: ( message.samples.dataset.values[0] + message.samples.dataset.values[0] ) / 2 });
    });

    // Map action to a hapi server method

    server.action('generate', 'generate:id', { cache: { expiresIn: 1000, generateTimeout: 3000 } });

    // Map action to a hapi server method with custom generateKey method

    server.action('average', 'calc:average', { cache: { expiresIn: 1000, generateTimeout: 3000 } }, generateKey: function (message) {

        return 'average-' + message.samples.dataset.values[0] + ':' + message.samples.dataset.values[1];
    });

    // Start hapi server (starts cache)

    server.start(function () {

        // Invoke server method

        server.methods.generate(function (err, result1) {

            // Invoke the same server method

            server.methods.generate(function (err, result2) {

                // result1 === result2 (cached)

                server.methods.average({ samples: { dataset: values: [2, 3] } }, function (err, avg1) {

                    server.methods.average({ samples: { dataset: values: [2, 3] } }, function (err, avg2) {

                        // avg1 == avg2 (cached)
                    });
                });
            });
        });
    });
});

reply.act(pattern)

Sends back a handler response using the result of a Seneca action where:

  • pattern - the Seneca action called to generate the response.
const Chairo = require('chairo');
const Hapi = require('hapi');

const server = new Hapi.Server();
server.connection();
server.register(Chairo, function (err) {

    // Set up a Seneca action

    var id = 0;
    server.seneca.add({ generate: 'id' }, function (message, next) {

        return next(null, { id: ++id });
    });

    // Add route

    server.route({
        method: 'POST',
        path: '/id',
        handler: function (request, reply) {

            // Reply using a Seneca action

            return reply.act({ generate: 'id' });
        }
    });
});

In addition, the act handler shortcut is also provided:

server.route({
    method: 'POST',
    path: '/id',
    handler: { act: 'generate:id' }
});

reply.compose(template, context, [options])

Renders a template view using the provided template and context where:

  • template - the view engine template (same as the name used in reply.view()).
  • context - the context object used to render the template. Chairo provides a special key $resolve where you can map context variables to Seneca actions matching they key's value pattern. Each of the services mapped to keys is resolved and the resultant key value maps are copied to the context root before redering the template.
  • options - optional settings passed to reply.view().

It requires the vision plugin to be registered with Hapi.

const Chairo = require('chairo');
const Handlebars = require('handlebars');
const Vision = require('vision');
const Hapi = require('hapi');

const server = new Hapi.Server();
server.connection();
server.register([Chairo, Vision], function (err) {

    // set up a few Seneca actions

    server.seneca.add({ lookup: 'date' }, function (message, next) {

        return next(null, { date: (new Date()).toString() });
    });

    server.seneca.add({ load: 'user' }, function (message, next) {

        return next(null, { name: message.name });
    });

    // Set up a hapi view engine

    server.views({
        engines: { html: Handlebars },
        path: __dirname + '/templates'
    });

    // Add route

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

            // Setup context with both Seneca actions and simple keys

            var context = {
                $resolve: {
                    today: 'lookup:date',                   // Using string pattern
                    user: { load: 'user', name: 'john' }            // Using object pattern
                },
                general: {
                    message: 'hello!'
                }
            };

            // Reply with rendered view

            return reply.compose('example', context);
        }
    });
});

Using the template ./templates/example.html:

<div>
    <h1>{{today.date}}</h1>
    <h2>{{user.name}}</h2>
    <h3>{{general.message}}</h3>
</div>

In addition, the compose handler shortcut is also provided:

server.route({
    method: 'POST',
    path: '/id',
    handler: {
        compose: {
            template: 'example',
            context: {
                $resolve: {
                    today: 'lookup:date',
                    user: { load: 'user', name: 'john' }
                },
                general: {
                    message: 'hello!'
                }
            }
        }
    }
});