How and why I roll my own
   node.js framework
Agenda

• A very brief introduction to node.js
• Personal technical skill background
• What is the minimal features a web framework must have?
• From express to plain node and back to express again
• Framework features
• Live demo
• Why do I roll my own node.js framework?
A very brief
introduction to
Node.js
What is node.js?
Node.js is a none-blocking
I/O , event-driven server-
side javascript. It runs on
google’s v8 javascript
engine.
Um ...
???
What is node really?
Node is JavaScript on the server
and it’s blazing fast
Personal technical skill background
Front-end developer ->
Node.js ->
Rails ->
Codeigniter(Php) ->
Rails(Ruby) ->
Node.js(JavaScript)
What is the minimal features
a web framework must have?
A.K.A
the most
important feature
Router?
NO
Code generators?
NO
ORM, Helpers,
Templates,
Command line
console ...
NO, NO, NO!!!
It’s project structure!
Example project structure


              .
              |-- configs.js
              |-- app
              | |-- controllers
              | |-- models
              | `-- views
              |-- core
              `-- public
                 |-- css
                 |-- img
                 `-- js
My first try
Define controllers


              // Ex. users.js
              module.exports = {

               index : function ( req, res ){
                 //...
               },

               show : function ( req, res ){
                 //...
               },

               new : function ( req, res ){
                 //...
               },

               create : function ( req, res ){
                 //...
               }

                // ....
              };
Load controllers into a hash



  var controllers = {};

  require( 'fs' ).readdir( PATH_TO_CONTROLLERS, function( err, files ){
   if( err ) throw err;

   files.forEach( function( file ){
    if( /.js$/.test( file )){
      var controller = require( PATH_TO_CONTROLLERS + file );

          Object.keys( controller ).forEach( function ( action ){
           var name = file.replace( '.js', '' );

            // Ex. controllers[ 'users/index' ] = controller[ action ];
            controllers[ name + '/' + action ] = controller[ action ];
          });
      }
    });
  });
Start server



        require( 'http' ).createServer( function( req, res ){
         var handler = require( 'url' ).
                   parse( req.url, true ).
                   pathname.
                   replace( /(/$)/g, '' );

          if( controllers[ handler ]){
            try{
              controllers[ handler ]( req, res );
            }catch( err ){
              res.error( 500, err );
            }
          }else{
            res.error( 404 );
          }
        }).listen( PORT, HOST );
From Express to plain node
and back to Express again
Intro to connect and express




        Node    -> Ruby
        Connect -> Rack
        Express -> Sinatra
What is middleware
and How does it work?
•WSGI (Python)
•Rack (Ruby)
•PSGI and Plack (Perl)
•Hack2 / WAI (Haskell)
•JSGI & Jack (JavaScript)
•Ring (Clojure)
•EWGI (Erlang)
•PHP Rack (PHP)
app.configure( function (){
  app.use( express.bodyParser());
  app.use( express.methodOverride());
  app.use( app.router );
});
Source: http://www.godfat.org/slide/2011-08-27-rest-
core.html#slide68
Middlewares, good or bad?
Performance <---> Flexibility
Flexibility and
maintainability
won the battle
Final features
• MVC structure.
• Mongoose as ODM (mongodb) with validation.
• Lightening fast template parsing with thunder.
• RESTful routing, supports namespace and nested resource.
• Middlewares and 3rd party libs support.
   ◦ All connect and express middlewares compatible.
   ◦ Socket.io compatible.
• Assets pipeline.
• Cluster support.
• Comprehensive logger for debugging.
• Command line tools.
   ◦ Powerful generators for project prototyping.
   ◦ Model command line console.
   ◦ Project routes inspecttion.
Packages that help to build
a web framework
Flow control
View parser
Asset packer
Inflection
Flow control
     var flow = new Flow();

     // set global variables
     flow.series( function ( next ){
       require( './lib/global' )( base_dir, next );
     }).

     // load logger
     series( function ( next ){
       require( CORE_DIR + 'logger' );
       // this has to go after the `require`
       LOG.sys( 'loading core module: logger' );
       next();
     }).

     // load util
     series( function ( next ){
       LOG.sys( 'loading core module: utils' );
       require( CORE_DIR + 'utils' );
       next();
     }).

     // load model
     series( function ( next ){
       LOG.sys( 'loading core module: model' );
       require( CORE_DIR + 'model' )( next );
     }).

     // load express
     series( function ( next ){
// load express
series( function ( next ){
  LOG.sys( 'loading core module: express' );
  require( CORE_DIR + 'express' )( next );
}).

// load lib
parallel( function ( results, ready ){
 var app = results[ 0 ];

  LOG.sys( 'loading core module: lib' );
  require( CORE_DIR + 'lib' )( app, ready );
}).

// load helper
parallel( function ( results, ready ){
 var app = results[ 0 ];

  LOG.sys( 'Loading helper: application' );
  require( HELPER_DIR + 'application' )( app );
  ready();
}).

// load assets
parallel( function ( results, ready ){
 var app = results[ 0 ];

  LOG.sys( 'loading core module: assets' );
  require( CORE_DIR + 'assets' )( app, ready );
}).

// overwrite res.send
parallel( function ( results, ready ){
 var app = results[ 0 ];

 LOG.sys( 'Overwriting express res.send' );
// overwrite res.send
         parallel( function ( results, ready ){
          var app = results[ 0 ];

           LOG.sys( 'Overwriting express res.send' );
           require( CORE_DIR + 'response' );
           ready();
         }).

         join().

         //load controller_brige
         series( function ( results, next ){
          var app = results[ 0 ][ 0 ];

           LOG.sys( 'loading core module: controller_bridge' );
           require( CORE_DIR + 'controller_bridge' )( app, next );
         }).

         // start server
         series( function ( app, next ){
           LOG.sys( 'loading core module: server' );
           require( CORE_DIR + 'server' )( app, next );
         }).

         // load 'after server start libs'
         end( function ( app ){
           LOG.sys( 'loading core module: started' );
           require( CORE_DIR + 'started' )( app );
         });




https://github.com/dreamerslab/node.flow
View parser

        <!DOCTYPE html>
        <html>
         <head>
          <title><?= it.title ?></title>
         </head>
         <body>
          <h1 class="header"><?= it.header ?></h1>
          <ul class="list">
          <? for( var i = 0, l = it.list.length; i < l; i++ ){ ?>
           <li class="item">'<?= it.list[ i ] ?>'</li>
          <? } ?>
          </ul>
         </body>
        </html>




https://github.com/dreamerslab/thunder
Asset packer
https://github.com/dreamerslab/node.packer




Inflection
https://github.com/dreamerslab/node.inflection
Yes,
most of the features
are from Rails
So why not just Rails?
Rails is quite slow
compare to node
Both
dev and production
env are hard to setup
***Dude I just want to write some code***
Backward compatibility is



       ‘0’
Don't get me wrong,
I still love its
architecture
and most of the
conventions
Best part of node
It’s a lot like
PHP in many ways
The language itself is
simple( but powerful )
You can start with
plain node and still
get your apps to work
very quickly
Depends on how complicated
your app is you can choose
different frameworks
builds on top of node
Live demo :D
Why I roll my own node.js
framework?
It’s fun !
You will definitely learn a lot in the process even if your framework is
not as popular as rails you should still give it a try. It really helps you
understanding how a framework work so that you can write better app
code.
Happy coding :)
Ben Lin
 ben@dreamerslab.com
 http://twitter.com/dreamerslab
 https://github.com/dreamerslab

How and why i roll my own node.js framework

  • 1.
    How and whyI roll my own node.js framework
  • 2.
    Agenda • A verybrief introduction to node.js • Personal technical skill background • What is the minimal features a web framework must have? • From express to plain node and back to express again • Framework features • Live demo • Why do I roll my own node.js framework?
  • 3.
  • 4.
  • 5.
    Node.js is anone-blocking I/O , event-driven server- side javascript. It runs on google’s v8 javascript engine.
  • 6.
  • 7.
  • 8.
    What is nodereally?
  • 9.
    Node is JavaScripton the server
  • 10.
  • 11.
  • 12.
    Front-end developer -> Node.js-> Rails -> Codeigniter(Php) -> Rails(Ruby) -> Node.js(JavaScript)
  • 13.
    What is theminimal features a web framework must have?
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
    Example project structure . |-- configs.js |-- app | |-- controllers | |-- models | `-- views |-- core `-- public |-- css |-- img `-- js
  • 23.
  • 24.
    Define controllers // Ex. users.js module.exports = { index : function ( req, res ){ //... }, show : function ( req, res ){ //... }, new : function ( req, res ){ //... }, create : function ( req, res ){ //... } // .... };
  • 25.
    Load controllers intoa hash var controllers = {}; require( 'fs' ).readdir( PATH_TO_CONTROLLERS, function( err, files ){ if( err ) throw err; files.forEach( function( file ){ if( /.js$/.test( file )){ var controller = require( PATH_TO_CONTROLLERS + file ); Object.keys( controller ).forEach( function ( action ){ var name = file.replace( '.js', '' ); // Ex. controllers[ 'users/index' ] = controller[ action ]; controllers[ name + '/' + action ] = controller[ action ]; }); } }); });
  • 26.
    Start server require( 'http' ).createServer( function( req, res ){ var handler = require( 'url' ). parse( req.url, true ). pathname. replace( /(/$)/g, '' ); if( controllers[ handler ]){ try{ controllers[ handler ]( req, res ); }catch( err ){ res.error( 500, err ); } }else{ res.error( 404 ); } }).listen( PORT, HOST );
  • 27.
    From Express toplain node and back to Express again
  • 28.
    Intro to connectand express Node -> Ruby Connect -> Rack Express -> Sinatra
  • 29.
    What is middleware andHow does it work?
  • 30.
    •WSGI (Python) •Rack (Ruby) •PSGIand Plack (Perl) •Hack2 / WAI (Haskell) •JSGI & Jack (JavaScript) •Ring (Clojure) •EWGI (Erlang) •PHP Rack (PHP)
  • 31.
    app.configure( function (){ app.use( express.bodyParser()); app.use( express.methodOverride()); app.use( app.router ); });
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
    • MVC structure. •Mongoose as ODM (mongodb) with validation. • Lightening fast template parsing with thunder. • RESTful routing, supports namespace and nested resource. • Middlewares and 3rd party libs support. ◦ All connect and express middlewares compatible. ◦ Socket.io compatible. • Assets pipeline. • Cluster support. • Comprehensive logger for debugging. • Command line tools. ◦ Powerful generators for project prototyping. ◦ Model command line console. ◦ Project routes inspecttion.
  • 38.
    Packages that helpto build a web framework
  • 39.
  • 40.
    Flow control var flow = new Flow(); // set global variables flow.series( function ( next ){ require( './lib/global' )( base_dir, next ); }). // load logger series( function ( next ){ require( CORE_DIR + 'logger' ); // this has to go after the `require` LOG.sys( 'loading core module: logger' ); next(); }). // load util series( function ( next ){ LOG.sys( 'loading core module: utils' ); require( CORE_DIR + 'utils' ); next(); }). // load model series( function ( next ){ LOG.sys( 'loading core module: model' ); require( CORE_DIR + 'model' )( next ); }). // load express series( function ( next ){
  • 41.
    // load express series(function ( next ){ LOG.sys( 'loading core module: express' ); require( CORE_DIR + 'express' )( next ); }). // load lib parallel( function ( results, ready ){ var app = results[ 0 ]; LOG.sys( 'loading core module: lib' ); require( CORE_DIR + 'lib' )( app, ready ); }). // load helper parallel( function ( results, ready ){ var app = results[ 0 ]; LOG.sys( 'Loading helper: application' ); require( HELPER_DIR + 'application' )( app ); ready(); }). // load assets parallel( function ( results, ready ){ var app = results[ 0 ]; LOG.sys( 'loading core module: assets' ); require( CORE_DIR + 'assets' )( app, ready ); }). // overwrite res.send parallel( function ( results, ready ){ var app = results[ 0 ]; LOG.sys( 'Overwriting express res.send' );
  • 42.
    // overwrite res.send parallel( function ( results, ready ){ var app = results[ 0 ]; LOG.sys( 'Overwriting express res.send' ); require( CORE_DIR + 'response' ); ready(); }). join(). //load controller_brige series( function ( results, next ){ var app = results[ 0 ][ 0 ]; LOG.sys( 'loading core module: controller_bridge' ); require( CORE_DIR + 'controller_bridge' )( app, next ); }). // start server series( function ( app, next ){ LOG.sys( 'loading core module: server' ); require( CORE_DIR + 'server' )( app, next ); }). // load 'after server start libs' end( function ( app ){ LOG.sys( 'loading core module: started' ); require( CORE_DIR + 'started' )( app ); }); https://github.com/dreamerslab/node.flow
  • 43.
    View parser <!DOCTYPE html> <html> <head> <title><?= it.title ?></title> </head> <body> <h1 class="header"><?= it.header ?></h1> <ul class="list"> <? for( var i = 0, l = it.list.length; i < l; i++ ){ ?> <li class="item">'<?= it.list[ i ] ?>'</li> <? } ?> </ul> </body> </html> https://github.com/dreamerslab/thunder
  • 44.
  • 45.
    Yes, most of thefeatures are from Rails
  • 46.
    So why notjust Rails?
  • 47.
    Rails is quiteslow compare to node
  • 48.
    Both dev and production envare hard to setup
  • 49.
    ***Dude I justwant to write some code***
  • 50.
  • 51.
    Don't get mewrong, I still love its architecture and most of the conventions
  • 52.
  • 53.
    It’s a lotlike PHP in many ways
  • 54.
    The language itselfis simple( but powerful )
  • 55.
    You can startwith plain node and still get your apps to work very quickly
  • 56.
    Depends on howcomplicated your app is you can choose different frameworks builds on top of node
  • 57.
  • 58.
    Why I rollmy own node.js framework?
  • 59.
  • 60.
    You will definitelylearn a lot in the process even if your framework is not as popular as rails you should still give it a try. It really helps you understanding how a framework work so that you can write better app code.
  • 61.
  • 62.
    Ben Lin ben@dreamerslab.com http://twitter.com/dreamerslab https://github.com/dreamerslab