Best practice for structuring libraries to be required in node.js

Endymion picture Endymion · Jul 30, 2013 · Viewed 10k times · Source

I have several utility libraries which contain helper functions and I want to load them in order they can be used from the controllers and I'm wondering what is the best practice for coding utility libraries in node.

I'm little bit confused because there are several ways to do it and I'm not sure what is the best/more suitable/more reliable. Here are 2 options but I'm wondering if they are the best (for example I've seen snippets that use module.exports = exports = function(){}, etc)

//option1.js


"use strict";

module.exports =  function(){

     exports.test1 =  function(){ console.log('hi I'm test1')};
     exports.test2 =  function(){ console.log('hi I'm test2')};
     return exports;
};

//option2.js

"use strict";

module.exports =  {

     test1 : function(){ console.log('soy test1')},
     test2 :  function(){ console.log('soy test2')}

};

//test_controller.js

/* Requiring helpers in different ways */
var option1 = require('./option1.js')();
var option2 = require('./option2.js');

Answer

Peter Lyons picture Peter Lyons · Jul 30, 2013

I think of my files in 3 sections:

Section 1: CommonJS Dependencies

var lib1 = require("lib1");
var lib2 = require("lib2");

You don't need any additional wrapper functions. All node modules are automatically wrapped by node.js in a function and doing so has no benefits and just adds clutter

Section 2: Plain JavaScript Code

This should be almost exclusively functions with a sprinkling of supporting variables or top level module code if needed.

var MY_CONST = 42;

function helper1() {
    //excellent code here
}

function helper2() {
    //excellent code here
}

Keep section 2 pure JS. Don't use commonJS idioms in this middle "pure" section. Don't use module, exports, require, etc. This is just my personal guideline as JS itself is stable but packaging in to modules is still under a lot of change and it's better to keep the CommonJS bits that are extraneous and likely to change separate from the interesting bits of code. ECMAScript 6 modules are most likely to replace CommonJS in a few years, so make this easier on yourself by keeping section 2 pure ECMAScript 5 and making an "CommonJS Sandwich™" as I like to call it.

Section 3: CommonJS exports

exports.helper1 = helper1;
exports.helper2 = helper2;
  • Putting all your exports at the end also gives you a quick way to understand what your public API is and prevents accidentally exporting properties due to a careless copy/paste.
  • I prefer the above exports.foo = foo; syntax as opposed to assigning module.exports to a new object literal. I find this avoids the trailing comma issue with the last property of an object literal.
  • Doing anything else with your require or exports statements is almost certainly unnecessary and needlessly slick or magic. Until you get to be advanced, don't do anything fancy here. (even then, if you're not TJ Holowaychuk, you're probably just being silly)

What should I export

A single function (@substack style)

function degreesToRadians(degrees) {}

module.exports = degreesToRadians;

Keep it small and simple.

An object of functions

If your module is a set of helper functions, you should export an object containing those functions as properties

var foo = require("foo");

function doubleFoo(value) {
  return foo(value) * 2;
}

function tripleFoo(value) {
  return foo(value) * 3;
}

exports.doubleFoo = doubleFoo;
exports.tripleFoo = tripleFoo;

A Constructor Function

If your module is a class design for object-oriented use, export the constructor function

function GoCart() {
  this.wheels = 4;
}

GoCart.prototype.drive = function drive() {
  //vroom vroom
}

module.exports = GoCart;

A Factory/Config Closure Function

Once you have mastered the above 2 patterns (really!) and feel confident exporting a factory function that takes options and maybe does some other dynamic stuff, go for it, but when in doubt, stick with the first 2, simpler choices.

//do-stuff.js
function doStuff(howFast, what) {
   return "I am doing " + what + " at speed " + howFast;
}

function setup(options) {
  //The object returned by this will have closure access to options
  //for its entire lifetime
  return {doStuff: doStuff.bind(null, options.howFast)};
}

module.exports = setup;

So you could use that like

var doStuff = require("./do-stuff")({howFast: "blazing speed"});
console.log(doStuff.doStuff("jogging"));
//"I am doing jogging at speed blazing speed"