IoC for Javascript & Node.js

Noder.io Quickstart

Noder.io provides a lightweight and flexible core to create a scalable API of a lib, a module, an application or a framework. Noder.io is inspired (among others) by Angular and Pimple.
It is useful for starting a project quickly with a modular API ready to use.

Noder.io (and any object built on top of Noder.io) integrates dependency injection, lazy loading and is extensible via a plugins system easy to use.

No dependencies, works on Node.js and in the browser (only 7kb).

Installation

For Node.js

You can install Noder.io with NPM (Node Package Manager).

npm install noder.io

For the browser

Although originally designed for use with Node.js, it can also be used directly in the browser (only 7kb).

See Noder.io in the browser.

Usage

var noder = require('noder.io');

Best practice, create an instance of Noder class:

// ./api/index.js
var Noder = require('noder.io').Noder;
var api   = new Noder();

// code body that constructs your API

module.exports = api;

or shortcut:

// ./api/index.js
module.exports = require('noder.io').createNoder();

Use your API in another file:

var api = require('./api');

// load a plugin
api.use('pluginName');

// create an item in the container
api.$di.set('someItem', 'value of the item');

// ...

Collection

// create a collection
var items = noder.createCollection();

items.set('myKey', 'my key value');

// my key value
console.log(items.get('myKey'));

// get all items
var all = items.getAll();

// true
console.log(items instanceof noder.Collection);

See collection.

Dependency Injection

See dependency injection.

Lazy loading

noder.$require method provides a lazy require():

// define the property without loading the mongoose module
noder.$require('mongoose');

// false
console.log(noder.$require.isLoaded('mongoose'));

// lazy loading
var mongoose = noder.mongoose;

// true
console.log(noder.$require.isLoaded('mongoose'));

// true
console.log(noder.mongoose === require('mongoose'));

Aliases:

noder.$require('promise', 'bluebird');

// true
console.log(noder.promise === require('bluebird'));

Custom loader:

// factory: promisify the "fs" module
noder.$require('fs', function() {
  return noder.promise.promisifyAll(require('fs'));
});

fs.readFileAsync('./any-file.js')
  .then(function(contents) {
    console.log(contents);
  })
  .catch(function(err) {
    console.error(err);
  })
;

See lazy loading.

Plugins

Noder.io provides a plugin system to make a package works as a plugin for Noder.io and also as a standalone module or library.

Example of a Noder plugin:

/**
 * Initialization for use as a standalone module.
 * @return {Noder} New `Noder` instance
 */
module.exports = function blog() {

  var Noder = require('noder.io').Noder;
  var noder = new Noder();

  // or use the shortcut:
  // var noder = require('noder.io').createNoder();

  return module.exports.__noder(noder);
};

/**
 * Init `blog` plugin.
 * @param  {Noder} noder  `Noder` instance
 * @return {Noder}        Current `Noder` instance
 */
module.exports.__noder = function blogPlugin(noder) {

  // create config object only if not exists
  noder.$di.addOnce('config', {}, true);

  // sub-modules of blogPlugin
  // that add features to the instance of Noder
  noder.use(require('./api/article'));
  noder.use(require('./api/comment'));
  noder.use(require('./api/admin'));

  // Always return the instance of Noder to allow chaining
  return noder;
};

See plugins.

Example

Here is an example that could be a real case.

File: ./api/config

module.exports.__noder = function config(noder) {

  noder.$di.set('config' {
    connection: 'mongodb://localhost/blog',
    post: {
      limit: 20
    }
  });

  return noder;
}

File: ./api/db.js

var Post = require('../models/post.js');

module.exports.__noder = function db(noder) {

  // define custom loader
  noder.$require('mongoose', function() {

    var mongoose = require('mongoose');

    // note: `this` === `noder.$di._container`
    this.connectMongoose(mongoose);

    return mongoose;
  });

  // define a singleton
  noder.$di.singleton('connectMongoose', function connectMongoose(mongoose) {

    return mongoose.connect(noder.$di.get('config').connection);
  };

  // define a helper
  noder.$di.set('getPosts', function getPosts(callback) {

    var config = noder.$di.get('config');

    return Post
      .find({published: true})
      .sort({'date': -1})
      .limit(config.post.limit)
      .exec(callback);
  };

  return noder;
};

File: ./api/index.js

// module.exports is a singleton
module.exports = require('noder.io').createNoder();

File: ./app.js

var api = require('./api');

// define lazy loader with alias
api.$require('markdown', 'marked');

// init config
api.use('./api/config');

// init DB dependencies
api.use('./api/db');

// init blog dependencies
api.use('./api/blog');

// invoke the dependencies injected as arguments
api.$invoke(['view', 'getPosts'], function(view, getPosts) {

  getPosts(function(err, posts) {

    // `posts` will be of length 20 (see `config` object)
    for(var i in posts) {

      // parse markdown of each posts
      posts[i].contents = api.markdown(posts[i].contents);
    }

    // render posts
    view.render(posts);
  });
});

// another way, use the dependencies in the scope (`this`)
api.$apply(function() {

  this.getPosts(function(err, posts) {

    // `posts` will be of length 20 (see `config` object)
    for(var i in posts) {

      // parse markdown of each posts
      posts[i].contents = api.markdown(posts[i].contents);
    }

    // render posts
    this.view.render(posts);
  });
})

The workflow may be easier but it's to show an example using several things at once.

With a small modification, we could also obtain this way, file: ./app.js

var db = api.db;

db.connectMongoose();

// invoke this function with the dependency injected as argument
db('getPosts', function(getPosts) {

  getPosts(function(err, posts) {

    // `posts` will be of length 20 (see `config` object)
    api.view('blog/posts', err, posts);
  });
});

Free rein to your imagination and your favorites good practices :)

Learn

The learning curve to be productive with Noder.io is very short. The guide presents you a quick tour of some features of Noder.io, the API is fully documented.

I suggest you read the few pages of this guide in order to know and to effectively exploit the capabilities provided by Noder.io.

Try to handle the noder.$di container, creates a service, try the dependency injection, creates a plugin ("hello world"). And go start your next project with Noder.io :)

The concrete practice is the best for learning quickly!

Unit Tests

Noder.io is tested with Unit.js and Mocha.

Unit.js is a powerful and intuitive unit testing framework for Node.js and also for javascript in the browser. The core of Unit.js is built on top of Noder.io.

It's a unit testing framework that provides a awesome implementation of Noder.io with dependency injection in the scope (this) of some BDD methods (given(), when(), then(), ... ), the plugin system and all features of Noder.io!

License

MIT (c) 2013, Nicolas Tallefourtane.

Author

Noder.io is designed and built with love by Nicolas Tallefourtane.