Composing applications from plugins

So far in this chapter, we've looked at how to create plugins, how to use third-party plugins in our applications, how to add communication between plugins, and how to create dependencies between plugins.

Plugins are a great way of managing your code base by splitting it up into smaller, more manageable chunks of functionality. But like every design strategy, plugins have some downsides. The downsides are mainly in the form of extra overhead when it comes to our registering and configuring them for our applications. In fact, configuration alone can be a big headache for applications. Let's look at how we can mitigate this.

When you think back to our index.js entry file from the last example, we simplified this by pushing all our business logic to plugins, making for a very readable and easy-to-follow entry file for new users of our codebase.

But now let's think about how this scales. As more functionality is added, the number of plugins would obviously grow. As an application moves closer to production, we would also want to add features such as logging and caching, which would mean even more plugins and further configuration. For larger applications, we could quickly form a giant entry or index.js file comprised of large configuration objects for our servers, connections, and repeated nested calls to plugin.register().

Fortunately for us, in keeping with the theme of configuration-over-code and foresight from the hapi team, the server.compose() API was created in the very first version of hapi. This allows you to create and configure your entire server, connections (or packs as they were called then), and plugins, all from just a single configuration object.

As hapi grew and became more modular, server.compose() was removed, and the compose functionality was pushed to a module called glue (https://github.com/hapijs/glue).

Using glue, we can define our entire application from this single configuration object. This can be a hard concept to grasp without an example, so let's take our previous user store example, and reduce it to a single configuration object that can be passed to glue to compose our server for demonstrating how this works. If you want to run this example, don't forget to npm install glue first! Let's see what our entry file looks like now:

const Glue = require('glue');
const manifest = {
  server: {},
  connections: [
    { port: 1337, host: '127.0.0.1' }
  ],
  plugins: [
    { 'hapi-level': { path: './temp', config: { valueEncoding: 'json' } } },
    { './user-store.js': {} },
    { 'blipp': {} }
  ]
}
Glue.compose(manifest, { relativeTo: __dirname }, (err, server) => {
  server.start((err) => {
    console.log(`Server running at ${server.info.uri}`);
  });
});

Hopefully, it's clear from this example that all our server creation and configuration code has been moved to this new manifest object, which we then pass to glue.compose(). This then composes our server object from the configuration, and returns the server object as the second parameter of the compose callback function. On reading manifest, you might be able to spot the one-to-one mapping of configuration against the actual hapi methods, but let's go through the mappings here:

{
  server: {},
  // maps to `new Hapi.Server();`

  connections: [{ port: 1337, host: '127.0.0.1' }],
  // maps to server.connection({ port: 1337, host: '127.0.0.1' });

  plugins: [ … ]
  // maps to server.register([ … ]);
}

While the reduction in code isn't huge in this example as our server is still quite small, you can imagine this saving quite a considerable amount of code when you have multiple connections and plugins registered to each.

Moreover, a command-line tool that uses glue internally can be used to launch a hapi server just from the manifest configuration object called rejoice (https://github.com/hapijs/rejoice). With this, instead of using glue within our application, we can just export the manifest object from our index.js file, and then use rejoice from the command line to compose and launch our server.

Again, this is best demonstrated by an example. So, let's modify the preceding example so that instead of passing our manifest object to glue.compose(), we export it using module.exports to be able to launch the server directly from the command line using rejoice:

const manifest = {
  server: {},
  connections: [
    { port: 1337, host: '127.0.0.1' }
  ],
  plugins: [
    { 'hapi-level': { path: './temp', config: { valueEncoding: 'json' } } },
    { './user-store.js': {} },
    { 'blipp': {} }
  ]
};
module.exports = manifest;

Hopefully, this is still easy to follow. The only change in this example from the previous one is that this time we used module.exports to export the manifest object instead of passing it to glue.compose().

Next, we need to be able to run rejoice from the command line. So, let's first install it using the following command:

$ npm install rejoice

This installs rejoice along with the commandline interface (CLI) tool within our node_modules folder. Usually, CLI tools in npm packages are located within the /bin directory of a module, and rejoice is no different. To launch our application, now we use the following command:

$ ./node_modules/rejoice/bin/rejoice -c index.js -p ./

This command does a number of things, so let's explain them. First, it calls rejoice; it also specifies index.js as the configuration object source using the –c flag, and then that our plugin files (in this case, the user-store.js file) are in the current directory, using the –p flag. All going well, if you run this command, you will get the following:

Composing applications from plugins

A couple of notes on this: instead of creating your server object in a .js file and exporting it using module.exports, you could also pass in a JSON file directly. This is up to developer preference, as is using glue and rejoice at all. Plenty of projects I've seen don't use either, but as projects grow, glue and rejoice are a great way of keeping your entry file simple, so it's good to be aware of them.

Now, you also might have found typing ./node_modules/rejoice/bin/rejoice constantly to launch your application frustrating. Fortunately, with npm scripts we have some alternatives to this, and we will explore these in detail in the next chapter on testing when we look at setting up our test scripts.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.108.119