Skip to content

Latest commit

 

History

History
285 lines (191 loc) · 10.4 KB

README.md

File metadata and controls

285 lines (191 loc) · 10.4 KB

reelyActive's Node.js Guide

Here we summarise the best practices, coding standards and tools for all our Node.js development.

Existing Standards

Fortunately, we aren't the first to code for Node.js, and there are some good style guides out there from which to draw inspiration. Remember, the reason for following (de facto) standards is to facilitate both collaboration and consistency.

Felix's Node.js Style Guide

Read it on GitHub. Read all of it. Seriously. All of it. We'll wait patiently. Now that you've read it you'll recall and appreciate the select highlights below:

Two Spaces

Use 2 spaces for indenting your code and swear an oath to never mix tabs and spaces - a special kind of hell is awaiting you otherwise.

80 Characters Per Line

Limit your lines to 80 characters. Yes, screens have gotten much bigger over the last few years, but your brain has not. Use the additional room for split screen, your editor supports that, right?

Case

  • Use lowerCamelCase for variables, properties and function names
  • Use UpperCamelCase for class names
  • Use UPPERCASE for Constants

Google's JavaScript Style Guide

Read it. You can skim through the bits that Felix already touched upon, because in the event of a conflict, Felix's Style Guide shall prevail. Please take extra care in reading the following three sections:

Naming

The very explicit Naming Style Rules provide more than simple DOs and DONTs, they also explain why the given practices are encouraged. They also reinforce Felix's naming guidelines, for example:

  • functionNamesLikeThis,
  • variableNamesLikeThis,
  • ClassNamesLikeThis,
  • EnumNamesLikeThis,
  • methodNamesLikeThis,
  • CONSTANT_VALUES_LIKE_THIS,
  • foo.namespaceNamesLikeThis.bar, and
  • filenameslikethis.js

Code Formatting

The very explicit Code Formatting Style Rules provide all the examples you need to resolve ambiguities. Keep in mind that Google follows the C++ formatting rules in spirit. How about an example pertaining to a common source of contention: Function Arguments?

When possible, all function arguments should be listed on the same line. If doing so would exceed the 80-column limit, the arguments must be line-wrapped in a readable way. To save space, you may wrap as close to 80 as possible, or put each argument on its own line to enhance readability. The indentation may be either four spaces, or aligned to the parenthesis.

Comments

The very explicit Comments Style Rules also follow the C++ rules in spirit and specifically use JSDoc. There are many illustrative examples. Read them! And remember:

Inline comments should be of the // variety.

reelyActive Standards

The existing standards above cover a lot, but they don't cover everything. So we're establishing a few of our own internal standards which are specifically suited to our projects and workflow.

Coding Style

Copyright

Every JavaScript file should start with the following comment:

/**
  * Copyright reelyActive 2014-2015
  * We believe in an open Internet of Things
  */

The use of a copyright indicates that the code is intellectual property created by reelyActive.

Note that the date range should extend between the year the file was initially created to the year the file was last modified. This is a simple way to provide a brief historical perspective of the code.

The line "We believe in an open Internet of Things" serves as a reminder of our vision and should inspire the developer to maintain the code in a way befitting of an open project with many collaborators.

Self: what is this exactly?

What is this? In Mixu's Node Book, the this keyword is considered the #1 gotcha in V8 and JavaScript. Therefore, in a class constructor and its class methods, we always use a self variable to refer to the class object instance. In other words, the self unambiguously refers to the class instance.

A static method can accept an instance parameter in order to work on a specific class object instance.

The example below illustrates our use of the self variable and instance parameter. And this video clip represents an amusing meme you can use to recall our motivation for the self variable.

/**
 * An example class constructor
 * @constructor
 */
function SomeClass() {
  var self = this;                               // Always on the first line

  self.app = express();
  self.httpPort = 80;

  self.app.listen(self.httpPort, function() {    // this.httpPort would work
    console.log("Listening on ", self.httpPort); // this.httpPort wouldn't work
  });
};

/**
 * An example class method
 */
SomeClass.prototype.doSomething = function() {
  var self = this;                               // Always on the first line

  someStaticMethod(self);
};


/**
 * An example static method
 * @param {SomeClass} instance The given class object instance.
 */
function someStaticMethod(instance) {
  instance.app.doSomethingElse();
};

RESTful Structure

Keep server.js simple

The class constructor is contained in a file named server.js. A developer should be able to quickly read the file and identify all the supported routes. Imagine it as a table of contents for the REST API.

Below is an example server.js file.

var express = require('express');

/**
 * An example RESTful class constructor
 * @constructor
 */
function RESTfulClass() {
  var self = this;

  self.app = express();

  self.app.use(function(req, res, next) {
    req.instance = self;                         // Sneak the class object
    next();                                      //   instance into the req
  });

  // Each route is in a separate external file
  self.app.use('/someroute', require('./routes/someroute'));
  self.app.use('/anotherroute', require('./routes/anotherroute'));

  self.app.listen(80);
};

module.exports = RESTfulClass;

One route per file

Each API route is in a separate external file within a routes subfolder. A developer should be able to quickly read the file and identify any middleware used, all the routes and subroutes as well as the supported HTTP methods for each.

Each route is an express router object.

Each method (GET, POST, PUT, DELETE, ...) for a given route will be implemented via a function call. This allows a developer to quickly navigate to the implementation of any given route. For clarity, the function name will have a prefix corresponding to the given HTTP method, specifically:

  • GET = retrieve
  • POST = create
  • PUT = replace
  • DELETE = delete

In keeping with the previous example, the file routes/someroute.js would resemble the following:

var express = require('express');
var router = express.Router();

router.use(function someMiddleware(req, res, next) {
  next();
});

router.route('/')                                // Supports GET, POST
  .get(function(req, res) {
    retrieveSomething(req, res);
  })
  .post(function(req, res) {
    createSomething(req, res);
  });

router.route('/:id')                             // Supports GET, PUT, DELETE
  .get(function(req, res) {
    retrieveSomethingSpecific(req, res);
  })
  .put(function(req, res) {
    replaceSomethingSpecific(req, res);
  })
  .delete(function(req, res) {
    deleteSomethingSpecific(req, res);
  });

function retrieveSomething(req, res) { }         // GET = retrieve
function createSomething(req, res) { }           // POST = create

function retrieveSomethingSpecific(req, res) { } // GET = retrieve
function replaceSomethingSpecific(req, res) { }  // PUT = replace
function deleteSomethingSpecific(req, res) { }   // DELETE = delete

module.exports = router;

Development Tools

We use a handful of development tools to ensure that our code is consistent, tested and documented, thereby making everyone's life easier.

JSHint for Standards Enforcement

See JSHint. Place the .jshintrc file included in this repository in the root folder of the project. To validate a file named server.js run:

jshint server.js

Give yourself a pat on the back when jshint returns no errors.

jscs for Code Style Enforcement

See jscs. Place the .jscsrc file included in this repository in the root folder of the project. To validate a file named server.js run:

jscs server.js

Reward yourself with a beer when jscs returns No code style errors found.

Mocha for Tests

See Mocha. Create a test folder in the root with two subfolders:

  • functional
  • unit

Unit tests shall have a .test.js file matching the name of the .js file under test. Functional tests shall have a .test.js file named after the scenario under test. There shall be one scenario (describe) per file to keep things tidy.

The following is an example of /test/unit/server.test.js which would represent a unit test for the file /lib/server.js:

var assert = require('assert');
var server = require('../../lib/server.js');  // Module under test

// Inputs for the scenario
var INPUT_DATA = 'coffee';

// Expected outputs for the scenario
var EXPECTED_DATA = { coffeeType: 'mocha' };

// Describe the scenario
describe('server', function() {

  // Set up the scenario (runs once at the start)
  before(function() {
    var self = this;
    self.server = new server();
  });

  // Reset the scenario (runs before each individual test)
  beforeEach(function() {
    var self = this;
    self.server.clear();
  });

  // Individual test (synchronous)
  it('should return an Object when instantiated', function() {
    var self = this;
    assert.equal(typeof self.server, 'object');
  })

  // Individual test (asynchronous)
  it('should return mocha when I ask for coffee', function(done) {
    var self = this;
    self.server.doSomethingAsynchronous(INPUT_DATA, function(data) {
      assert.deepEqual(EXPECTED_DATA, data);
      done();
    });
  })
});

JSDoc for Documentation

See JSDoc.