Saturday, March 14, 2015

Node.js Module Best Practices, NPM, and NVM

Node.js is an open-source runtime for executing javascript on the server-side. Leveraging the Google V8 JavaScript engine an asynchronous, non-blocking I/O library and a single-threaded event-based loop, web applications can run in realtime and support thousands of concurrent connections. This event based loop is what inspired Vert.x.

Modularity and Reusability

You're encouraged to write small modules with clear responsibilities and separation of concerns. This makes it easier for the community to share code with very little overhead. Node developers tend to follow similar conventions. Modules are defined with the following simple syntax:

'use strict' // Always add this to the top of your module

var foo = function() {
  return true;
};

module.exports = foo;

And are imported like so:

var foo = require('./foo.js');
foo(); // true

When you want to export multiple functions within a module, you have a few different options. In the following example, we return an object and do away with the variable assignment altogether.

'use strict'

module.exports = {
  bar: function() {
    return true;
  },
  baz: function() {
    return false;
  } 
};
var foo = require('./foo.js');
foo.bar(); // true
foo.baz(); // false

When in need of a more complex api, you're encouraged to return a factory (which in this case is an object) by utilizing the revealing module pattern. The name comes from the idea that you define everything as private in the constructor, and then return an object where you indicate which properties and methods you'd like to make public. By returning an object, you also have the side effect of not needing to instantiate your module with the keyword new. However, keep in mind, you're not returning the constructor so you won't be passing parameters on instantiation. See this example:

'use strict'

module.exports = function() {
  // private
  var bar = 0;

  var baz = function() {
    return true;
  };

  var qux = function() {
    return false;
  } 

  // public api
  return {
    bar: bar,
    baz: baz
  };
};
var foo = require('./foo.js');
var f = foo();
f.bar; // 0
f.baz(); // true
f.qux(); // error

Adding on top of what I was saying about modularity, you're also encouraged to write your modules and functions as if they were stateless, accepting parameters and returning a result. Of course, you can't always get away with this. While not encouraged with node modules, here's an example of when you need to return a constructor and in need of something more akin to a Class.

'use strict'

var Foo = function() {
  // private
  var self = this;
  var bar = 'a';
  var baz = 'b';

  // public api
  this.qux = function() {
    return true;
  } 

  get barbaz = function() {
    return bar + baz;
  };
};

module.exports = Foo;

In the above example, the public api is tied to the instance through this. We've capitalized Foo as a convention. Prior to ES6 and coffeescript, there was no designation for classes in JavaScript. Same as constants, you would capitalize the entire variable name as a convention to let other developers know not to mutate it, but there was nothing in place to prevent them from doing so, so it didn't truly behave like a constant. Instantiating this example would involve the new keyword:

var Foo = require('./foo.js');
var foo = new Foo();
foo.barbaz; // 0
foo.bar; // error
foo.baz; // error
foo.qux(); // true

You should also take note the use of the getter barbaz. It's a good practice not to directly modify variables attached to the instance. Rather, you should make use of getters and setters. This allows you to further insulate your variables, hide the internal representation of a value (getName() could actually be getting several fields for you), and you can perform validation. The getter convention we're using here was introduced in ES6, but is fully supported by node. See more about backwards compatible getters and setters here.

Before we move onto npm, let me give you some more guidelines to follow:

  • Loosely couple your modules
  • Don't reference globals
  • Avoid implicit stateful dependencies
  • Modules should be testable in isolation, outside of your app
  • Modules should behave like interchangeable plugins
  • Make your modules open to extension, closed to modification
  • Program to an Interface, not an implementation

NPM

npm was originally developed by Isaac Z. Schuleter out of frustrations he felt while working with CommonJS. More than just a module definition, npm also provides developers the npm registry, which makes it easy to share and reuse code. The npm team is a huge promoter of semantic versioning and other packaging and publishing standards.

While popular within the node community, you're encouraged to publish everything JavaScript, ES6, HTML and CSS to npm. So yes, client-side code, also. The npm registry is widely touted as having an "unbeatable ecosystem". All this means is that the registry is massive and is growing at a very high rate. You could say the same about github and docker's registries.

NVM

nvm stands for node version manager, which makes it easy to install and isolate multiple versions of node on an operating system. This is doing for node what virtualenv does for python, Ant, Maven and gradle do for Java, and RVM for ruby.

See the nvm repo for instructions on how to install it. The most common commands that go with my workflow are as follows:

# open a terminal window and list...
# ...available node versions
nvm ls
->  v0.10.33
      system
stable -> 0.10 (-> v0.10.33) (default)

# use specified version
nvm use v0.10.33

# good to go
node --version
0.10.33

That concludes this article. To learn more about some other hands-on concepts like the package.json file, see this guide.

No comments:

Post a Comment