hapi.js: Should It Be A Microservice? Decide Later

Talking Heads -- Once in a Lifetime

When building out an API for a service, you may find yourself building at least parts of an API you’ve already written before.

You may find yourself
In another part of the world

This is often the time you should write it as a microservice and share it between several apps, right?

Letting the days go by
Let the water hold me down

Well, if you’re writing your API in NodeJS with hapi.js, you could just publish your microservice as a private module, and decide later.

You may ask yourself
Well, how did I get here?

Try using a controller pattern, and loading the controller as a hapi.js plugin.

You may ask yourself
How do I work this?

The root of an npm package is typically index.js, although you can change this in package.json by setting “main” to another file. Set up your server.js as the script that loads all of your hapi.js plugins and starts the HTTP service, and setup index.js as a hapi.js plugin that takes your configuration as options and sets up all of the routes.

Letting the days go by
Water flowing underground

Let’s say that one of the requirements for your project was the ability to have user comments.

./package.json

{
  "name": "@fritzy/comments",
  "version": "0.1",
  "main": "index.js"
}

./server.js

'use strict';

const config = require('getconfig');
const Hapi = require('hapi');

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

server.register([
  {
    register: require('good'),
    options: {
      reporters: [{
        reporter: require('good-console'),
        events: {
          response: '*',
          log: '*'
        }
      }]
    },
  },
  {
    // here we require our microservice as a controller
    // you'd require('@fritzy/comments') in any other project
    register: require('./index'),
    options: config
  }
], (err) => {

  if (err) {
    throw err; // something bad happened loading the plugin
  }
  server.start((err) => {

    if (err) {
       throw err;
    }
    server.log('info', 'Server running at: ' + server.info.uri);
  });
});

./index.js

'use strict';

const Comments = require('./handlers/comments');

// options is the config that we passed in server.js
module.exports = function (plugin, options, next) {

  const db = require('pg')(options.db);
  plugin.bind({
    db: db
  });

  plugin.route({
    method: 'get',
    path: '/',
    config: Comments.list
  });
  plugin.route({
    method: 'get',
    path: '/{id}',
    config: Comments.get
  });
  plugin.route({
    method: 'post',
    path: '/',
    config: Comments.create
  });
  plugin.route({
    method: 'put',
    path: '/{id}',
    config: Comments.update
  });
  plugin.route({
    method: 'delete',
    path: '/{id}',
    config: Comments.delete
  });  

};

And you may ask yourself
Where is that large automobile?

Now you can publish this project as a private npm package, and require it in your larger project. In fact, you can require it any of your future projects whenever you need comments. If you decide later to make it a microservice, simply deploy it by itself (running the server.js).

And you may tell yourself
This is not my beautiful house

When you register the @fritzy/comments in your larger project’s server.js , here is what it’ll look like:

server.register([
  // ...
  {
    register: require('@fritzy/comments'),
    routes: {
      prefix: '/comments'
    },
    options: config
  }
]);

And you may tell yourself
This is not my beautiful wife

You now have a lot of extra flexibility in scaling, re-using code, and production operations styles for your microservice/module/plugin.

Same as it ever was
Same as it ever was


If you have any thoughts or feedback, poke me on Twitter @fritzy.

We'd love to help you with your NodeJS project, so drop us a line.

Enjoy this post? We'd love to invite you to join our mailing list, &you, where we connect with our community and share the latest we're learning.

You might also enjoy reading: