nodulejs
NOTE: This is still very much an active repo. We just haven't needed to change anything in a while. Any feature requests, issues or inquiries will be answered promptly.
nodulejs is a lightweight utility based on node/express, whose sole purpose is to discover and initialize web components (standard AMD Javascript objects) called "nodules". Nodules are tied to one or more express routes, and attached to each incoming express request as req.nodule.
A nodule looks like this:
module { return route: '/home' middlewares: getProfile getMoreData ; var { // make call to an API or database }; var { // make another async DB/API call which is dependent on getProfile // do business logic on returned data // render template };};
Which has the exact same behavior as this:
// filename app/myModule.jsmodule { app; var { // make call to an API or database }; var { // make another async DB/API call which is dependent on getProfile // do business logic on returned data // render template };}; // in app.js or some sub component:;
But notice that in the nodulejs example, it is not necessary to require the component myModule.js from another file. The nodule is discovered automatically by the nodulesjs framework - based on configurable file search patterns. This makes it less onerous to reorganize code. Renaming files, moving files, creating subdirectories are all much easier because there is no path to maintain off in another file.
Conversely a nodule's route is not tied to its filename or path. We feel this is the best of both worlds - as files can be renamed and moved w/o impacting the back end or front end code. Developers are more likely to rename and reorganize files - which results in tigher, more self-explanatory code. Each nodule is roughly 1-1 with a web route (although it can serve multiple routes). Our experience is that in large distributed teams, this separation of features into bite-sized files aids development considerably.
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:
- 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'] }] - debugToConsole: (OPTIONAL, default=false) set to true to see nodulejs debug output in the console
- customDebug: (OPTIONAL) custom debug function
example: function(identifier) { return function(msg){ myDebug(msg) } }
There are 4 local properties unique to each nodule:
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:
- route: (REQUIRED) one or more express routes - can be a string, RegExp, or array of either
- routeVerb: (OPTIONAL, default=get) get, post, put, del
- routeIndex: (OPTIONAL, default=0) use to load routes before or after others, can be negative, like z-index
- 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.
*nodulejs can be a building block for more complex behaviors. See the yukon component framework for an example.
To run node tests
Download nodulejs - https://github.com/jackspaniel/nodulejs/archive/master.zip
$ npm install
$ make test
To see the Demo App in action outside of test mode
node app
Examples
Basic page
(homePage.js from the demoApp)
module { return route: '/' '/home' '/special' { thistemplateName = reqpath > -1 ? 'altHomePage.jade' : 'homePage.jade'; } ;};
Form submit
(submitForm.js from the demoApp)
module { return route : '/json/submitForm' routeVerb: 'post' { thisdbParams = param1: reqbody ? reqbodyparam1 : null; // in real life don't forget to sanitize query params! } { if reqnoduleresponseDatadbMsg === -1 thiscustomMsg = 'Form submit failed, please supply valid param1'; } ;};
Catch-all page
(404 error nodule - shows routeIndex and one-off middleware)
module { return route: '*' routeIndex: 1000 // high routes are registered last middlewares: { reqnodule; res; } ;};
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: { var strRoute = noduleroute; if nodulerouteVerb === 'post' return doPreForm doPostForm sendJsonResponse; else if strRoute === 0 return doBusinessLogic sendJsonResponse; else return doBusinessLogic sendHtmlResponse; } // custom properties on top of the nodulejs core properties templateName: 'default.jade' templateDir: null { } ;
Middleware which calls nodule-level business-logic function
(from demoApp.js)
{ ; // app-level business logic can go here reqnodule; // app-level business logic can also go here ;}
Multiple middleware functions which make an asynchronous call to the DB
(from demoApp.js - goes with Form submit example above)
... { if nodulerouteVerb === 'post' return doPreForm doPostForm sendJsonResponse;... { reqnodule; ;} { reqnodule; ;} // DB simulator, see /json/formSubmit.js { var response = callparamsparam1 ? 'valid data, param1='+callparamsparam1 : 'missing param1, please resubmit'; call;}