Code Monkey home page Code Monkey logo

nodulejs's Introduction

nodulejs

Gitter NPM Version NPM Downloads Build status Test coverage

nodulejs is a lightweight utility based on node/express, whose sole purpose is to discover and initialize web components (standard Javascript objects) called "nodules". Nodules are tied to one or more express routes, and attached to each incoming express request as req.nodule.

For a fully fleshed-out implementation of nodulejs, see the yukon component framework.

Installation

$ npm install nodulejs

Usage

require('nodulejs')(app, config); 

app = express instance
config = any custom properties you want to add or defaults you want to override, see the demoApp

There are 3 global config properties:

  1. dirs: (OPTIONAL, default='/nodules') path(s) to look for your nodules, exclude property can be full or partal match
    example: [{ path: '/app', exclude: ['demoApp.js', '.test.js', '/shared/'] }, { path: '/lib/nodules', exclude: ['.test.js'] }]
  2. debugToConsole: (OPTIONAL, default=false) set to true to see nodulejs debug output in the console
  3. customDebug: (OPTIONAL) custom debug function
    example: function(identifier) { return function(msg){ myDebug(msg) } }

What is a nodule?

A nodule is a self-discovering, self-initializing component that would be analogous to a JSP or PHP page in those worlds. Except it has an advantage in that its route is declared, not tied by default to the file name or file structure. So you are free to re-organize nodules without upsetting urls. But more importantly, because nodules are self-discovering, there are no onerous config files to maintain (IE - Spring). This system allows a much more scalable architecture on large sites--as there are no config or other shared files which grow to enormous sizes as the site grows, and nodules can be re-organized, placed into sub-folders, etc. with zero impact.

What does a nodule do?

Not a whole lot out of the box. See the yukon component framework for a fully-fleshed out implementation. I split nodulejs off from yukon with the idea that it can potentially be a building block for other frameworks.

A nodule can have any properties you want to add, which will be propagated throughout the middleware chain as as req.nodule. But nodulejs only cares about 4 core properties, which are needed to register express middleware at app-init time:

  1. route: (REQUIRED) one or more express routes - can be a string, RegExp, or array of either
  2. routeVerb: (OPTIONAL, default=get) get, post, put, del
  3. routeIndex: (OPTIONAL, default=0) use to load routes before or after others, can be negative, like z-index
  4. middlewares: (OPTIONAL but your app isn't going to do much w/o them) an array of middleware functions to call for each nodule, or function(nodule){...} which returns said array. This array can be globally static for all nodules, semi-global based on rules (by using the function option), or specified one-off within each nodule.

Ok, what problem does this solve?

From a feature-development point of view, we wanted to give developers the flexibility of component-based architecture as much as possible, but still keep system-wide control over the middleware chain. On a small site with a small development team the latter might not be an issue. But on a large site with devs scattered all over the globe, some kind of middleware sandbox was a necessity.

Our feature devs spend 80-90% of their effort in jade templates or on the client side. For them, node components are often just a pass-through to the API(s), with some business logic applied to the request on the way in, and API data on the way out. Ideally they should have to learn the as little as possible of the vagaries/plumbing/whatever-your-favorite-metaphor-for-framework-stuff of node. Creating a new node component should be as easy for them as creating a new JSP - but again, without the framework losing control of the middleware chain.

From a framework-development point of view, we knew that as requirements evolved, we would constantly need to add default properties to each component, while hopefully causing as little disruption as possible to existing components. This is easily accomplished by adding a default property to the base config, then specifying the property only in the nodules that need the new property.

We also knew we'd need to add slices of business logic globally or semi-globally at any point in the request chain. By keeping control of the middleware chain we are able to do this with ease.

This diagram, from the fully-fleshed out yukon component framework might make the concept a little more clear:

To Run Node Tests

Download nodulejs - https://github.com/jackspaniel/nodulejs/archive/master.zip
$ npm install
$ make test 

Examples

Basic page

(homePage.js from the demoApp)

module.exports = function(app) {
  return {
    route: ['/', '/home', '/special'],
  
    doNoduleBusinessLogic: function(req, res) {
      this.templateName = (req.path.indexOf('special') > -1) 
                          ? 'altHomePage.jade' 
                          : 'homePage.jade';
    }
  };
};

Form submit

(submitForm.js from the demoApp)

module.exports = function(app) {
  return {
    route : '/json/submitForm',  

    routeVerb: 'post', 
    
    doPreFormBusinessLogic: function(req, res) {
      this.dbParams = {param1: req.body ? req.body.param1 : null}; // in real life don't forget to sanitize query params!
    },

    doPostFormBusinessLogic: function(req, res) {
      if (req.nodule.responseData.dbMsg.indexOf('valid data') === -1)
        this.customMsg = 'Form submit failed, please supply valid param1';
    }
  };
};

Catch-all page

(404 error nodule - shows routeIndex and one-off middleware)

module.exports = function(app) {
  return {
    route: '*',

    routeIndex: 1000, // high routes are registered last
  
    middlewares: [
      function(req, res, next) {
        req.nodule.debug('404 error middleware called!');
        res.send('<html><body><h1>404 error!</h1></body></html>');
      }
    ]
  };
};

Demo App config

(from demoApp.js - shows defining several nodule-dependent middleware chains at app init time, and adding extra nodule properties)

var config =  {
  
  dirs: [
    { path: myDir, exclude: ['demoApp.js', '.test.js'] },
  ],

  debugToConsole: true, 
  
  noduleDefaults: {

    middlewares: function(nodule) {
      var strRoute = nodule.route.toString();

      if (nodule.routeVerb === 'post') 
        return [doPreForm, doPostForm, sendJsonResponse];
      else if (strRoute.indexOf('/json') === 0) 
        return [doBusinessLogic, sendJsonResponse];
      else  
        return [doBusinessLogic, sendHtmlResponse];
    },
  
    // custom properties on top of the nodulejs core properties
    templateName: 'default.jade',

    templateDir: null,

    doNoduleBusinessLogic: function(req, res) { },
  },
};

Middleware which calls nodule-level business-logic function

(from demoApp.js)

function doBusinessLogic(req, res, next) {
  debug('doBusinessLogic middleware executed for: ' + req.nodule.name);

  // app-level business logic can go here

  req.nodule.doNoduleBusinessLogic(req, res);

  // app-level business logic can also go here

  next();
}

Multiple middleware functions which make an asynchronous call to the DB

(from demoApp.js - goes with Form submit example above)

...
    middlewares: function(nodule) {
      if (nodule.routeVerb === 'post') 
        return [doPreForm, doPostForm, sendJsonResponse];
...


function doPreForm(req, res, next) {

  req.nodule.doPreFormBusinessLogic(req, res);

  makeDbCall({
    params: req.nodule.dbParams, 
    callback: function(err, response) { 
      req.nodule.responseData = response;
      next(); 
    }
  });
}

function doPostForm(req, res, next) {
  
  req.nodule.doPostFormBusinessLogic(req, res);
  
  next();
}

// DB simulator, see /json/formSubmit.js
function makeDbCall(call) {
  var response = (call.params.param1) ? 'valid data, param1='+call.params.param1 : 'missing param1, please resubmit';
  call.callback(null, {dbMsg:response});
}

License

MIT

nodulejs's People

Contributors

yukonsaint avatar jackspaniel avatar giles-t avatar gitter-badger avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.