Four Kitchens
Insights

Make it modular

4 Min. ReadDevelopment

One of the most powerful features of node.js is its package manager, npm, and the wealth of modules it gives you access to. By using node modules you can leverage work done by other members of the community and spend more time focusing on solving your specific problem. There are other benefits to node modules though: they make your code more testable by splitting out non-critical logic, they make your code more maintainable by reducing the code paths through code you’ve written, and they improve the organization of your overall project.

Not all code that’s written can be published on npm though, so you might think you’re out of luck when it comes to making your own code into a node module. Fortunately, you’d be wrong. With a few small changes to your package.json file you can easily take advantage of node modules without needing to publish anything to npm.

If you’ve been doing node work for any amount of time, you’re probably familiar with the dependencies hash in package.json. Did you know that in lieu of a version you can use a git repository? Consider a module like this:

"dependencies": {
  "my-api-connector": "git+ssh://git@github.com:fourkitchens/my-api-connector#v0.1.1"
}

This will tell npm to use git with SSH keys to pull down the code at fourkitchens/my-api-connector that was tagged with v0.1.1. There are other ways to include git repositories as the source of your dependency but this method will allow you to pull down private code – assuming your deploy scripts and developers that are running npm install also have access to this repository. You can also replace the tag with a commit hash or even a branch, although for production code I would strongly advise tagging whatever you plan on releasing so you have greater certainty that the code you’re depending on is stable.

Now let’s look at an example of how we might use this. It’s common to make requests to an external API in node code:

var request = require('request');
var API_ROOT = 'https://my-api.com/';

request.get(API_ROOT + '/posts', function (err, res) {
  if (err) {
    return;
  }

  render(JSON.parse(res.body));
});

You can imagine that this pattern is going to quickly become repeated for every call out to this API. Furthermore, if we re-implement the actual calls to the API and later decided we wanted to change behavior of the underlying transport – perhaps by using connection pooling or even just a different library – we now need to make sure those changes get made all over our code base. So we could easily re-implement this in a reusable way like this:

my-api-connector.js

var request = require('request');
var API_ROOT = 'https://my-api.com/';

module.exports = {
  /**
   * GET a resource from the api.
   *
   * @param {string} resource
   *   The relative path to the resource.
   * @param {function} next
   *   The callback to execute when the resource has been fetched, accepts two parameters:
   *   - err: an error if one existed for the call.
   *   - res: A parsed response object.
   */
  get: function (resource, next) {
    request.get(API_ROOT + '/' + resource, function (err, res) {
      if (err || res.statusCode !== 200) {
        return next(err || res.statusCode);
      }

      next(null, JSON.parse(res.body));
    });
  }
};

You’ll notice that we’re also abstracting away error handling by status code and are parsing the JSON response from the API. This means that calling code doesn’t have to be at all concerned with the underlying transport and can instead just focus on error or response handling.

Now we can add the module to our codebase like any other node module:

my-code.js

var APIConnector = require('my-api-connector');

APIConnector.get('users', function (err, res) {
  if (err) {
    return;
  }

  render(res);
});

And going forward if we need to change any of the API connection behavior we don’t need to do so in our application code, as long as the callback from API methods maintains the same signature. Furthermore, when we need to start unit testing our application code we can very easily stub our custom module’s methods and focus wholly on testing our application code rather than external dependencies.

One final benefit of putting your reusable code into its own module versus just putting it into its own file is cleaner requires. For example instead of having something that looks like this:

var APIConnector = require('../../../helpers/my-api-connector');

You’ll have a much cleaner require:

var APIConnector = require('my-api-connector');

So take a look at the package.json documentation and start making your code more modular!