Isomorphic JavaScript Frameworks compared

Isomorphic What?

Client MVC

isomorph_mvc_client

The bulk of the application logic (views, templates, controllers, models, internationalization, etc.) lives in the client, and it talks to an API for data.

Maintainability

While the ideal case can lead to a nice, clean separation of concerns, inevitably some bits of application logic or view logic end up duplicated between client and server, often in different languages. Common examples are date and currency formatting, form validations, and routing logic. This makes maintenance a nightmare, especially for more complex apps.

Client + Server MVC

isomorph_mvc_client_server

In this world, some of your application and view logic can be executed on both the server and the client. This opens up all sorts of doors — performance optimizations, better maintainability, SEO-by-default, and more stateful web apps.

With Node.js, a fast, stable server-side JavaScript runtime, we can now make this dream a reality. By creating the appropriate abstractions, we can write our application logic such that it runs on both the server and the client — the definition of isomorphic JavaScript.

Source: Airbnb

Now on to the answers of the different frameworks on how to tackle true isomorph JavaScript.

Express + Mongoose

Mongoose is nice, but as of yet it is not fully isomorphic. Models get not shared to the client. Mongoose 4 will change that. But there are other shortcomings. Horrendous documentation. Missing features like persistent virtuals.

Model

  • Hand-Code Model yourself
  • No API visualisation
  • Yeoman and Grunt helps alot with creating automatic tests

    var DomainSchema = new Schema({    
        date          : { type: Date, default: Date.now},
        domain        : { type: String, default:''},
        totalviews    : { type: Number, default:0 },
        visitors      : [ 
            {
              owner      : { type: String, default:''},
              ip         : { type: String, default:''},
              views      : { type: Number, default:0 }
            }
          ]
    }, { collection: 'ips' })
       .set('toJSON',      
       { virtuals: false }, 
       { "strict": false }
    );
    
    
    module.exports = mongoose.model('Domain', DomainSchema);
    

Routes

In Mongoose and Express you have a lot of options but need to hand code everything. Yeoman is easing things quite a bit.

app.use('/user/',       require('./api/user'  ));

router.get('/date/:domain/:date', expressJwt({secret:secret.secretToken}), controller.find )

 exports.find = function(req, res, next) {
    var date = new Date(req.params.date); 
       return Domain.find({domain:req.params.domain})
              .where('date').equals(date)
              .exec(function (err, domain) {
                 if (!err) {
                    res.status(200).send({
                        domain:domain
           });
        };
    });
};

Quickly the need for Coffeescript arises

Client

On the client side the model only persists in Controllers where you inject the service and make a http request. There are Session Store Components available, yet you have to do this on your own. Mongoose RCv4 does change this and brings the model to the Browser's Sessionstore.

Sails

Sails leans more toward convention over configuration

Sails is an MVC framwork for node, which is good for single page apps. Sails rests on top of Socket.io and Epxress. Nothing is hidden like in Meteor. Socket.io uses websockets to enable communication between the server and client. Sails routes all HTTP requests through socket.io.

Model + API = One

Models also create a RESTful API along with it. Sails will subscribe you to updates on the database, your clients will always get notified of any changes on the model.

Route and Model creation

sails new ChatApp
cd ChatApp
sails generate model users
sails generate model messages
sails generate controller messages
sails generate controller main

This will create quite a few files. Open up config/router.js:

module.exports.routes = {
  '/' : {
     controller: 'main',
     action: 'index'
  },
  '/signup' : {
     controller: 'main',
     action: 'signup'
  },
  '/login' : {
     controller: 'main',
     action: 'login'
  },
  '/chat' : {
     controller: 'main',
     action: 'chat'
  }
};

You would now create routing functions as usual:

Middleware

login: function (req, res) {
  var username = req.param("username");
  var password = req.param("password");

  Users.findByUsername(username).done(function(err, usr) {
    if (err) {
        res.send(500, { error: "DB Error" });
    } else {
        if (usr) {
            var hasher = require("password-hash");
            if (hasher.verify(password, usr.password)) {
                req.session.user = usr;
                res.send(usr);
            } else {
                res.send(400, { error: "Wrong Password" });
            }
        } else {
            res.send(404, { error: "User not Found" });
        }
    }
  });
}

Client-Side

and on the client side you can use Angular if you prefer to.

Serve

$ sails lift

Deploy

You can use Grunt as usual.

Loopback

Loopback extends from express.js

App <-> API <-> DB

Loopback focuses on API creation. Loopback extends express - syntax is almost the same.

Model

When you create a model in Loopback it is created on the client and server.

$ slc lb model product -i

Open Model

for e.g. in development

  • No Schema
  • MongoDB
  • Memory
  • Data Sources

Strict Model

As you move along your dev model can become strict

  • Schema
  • Relational DBs

Attach to Data API

Like Hibernate, but directly on the model.

// crud operations
//
product.create({name: ...}); // inserts an object into DB

product.find( {
 where: {
  price: {
    lt:20, 
    limit:10
  }
 }

 // geo querys (supports all databases)
 //
 {
   geo: {
      near : {
        lat: 22.33, long: 44.55
      }
   }
 }

// count
// 
product.count({where: ..});

Create Model

$ npm install loopback -g
$ slc loopback app
$ npm install --save loopback-connector-mongodb
$ slc arc

Using Arc

arc

Explore API

Meteor

On Meteor, pretty much everthing is abstracted away

Truly isomorphic, JS files get shared to both client and server. Reloading the browser is unnecessary due to hot code push. Boilerplate coding is not necessary anymore, everything is build in.

Core design motivations

  • Data on the wire - instead of the server sending HTML to the client, Meteor only sends the minimum data necessary to re-render the portion of the page that has changed. This enables building low-latency single-page applications that avoid whole-page refresh. This works through subscribing to things on the server - a websocket.

  • One language - "JavaScript everywhere" (isomorphic JavaScript) makes it easier to acquire talent and learn Meteor

  • Database everywhere - the same API can be used on both the server and the client to query the database. In the browser, an in-memory MongoDB implementation called Minimongo allows querying a cache of documents that have been sent to the client.

  • Latency compensation - on the client, Meteor prefetches data and simulates models to make it look like server method calls return instantly.

  • Full stack reactivity - all layers, from database to template, update themselves automatically when necessary.

  • MiniMongo Sits on the Client. Meteor keeps it all in sync.

  • Reactivity Long ago asked for objects get updated automatically when the database changes.

Installation

§ export https_proxy:port; curl https://install.meteor.com/ | sh
§ meteor create simple-todos

Creating Authentication

Shell

$ meteor add accounts-ui accounts-password

HTML

{{> loginButtons}}

Controller

// At the bottom of the client code
Accounts.ui.config({
  passwordSignupFields: "USERNAME_ONLY"
});

Model

Tasks.insert({
  text: text,
  createdAt: new Date(),            // current time
  owner: Meteor.userId(),           // _id of logged in user
  username: Meteor.user().username  // username of logged in user
});

Accessing Model on client

{{currentUser.username}}

Deployment

$ meteor deploy my_app_name.meteor.com

Proxy

Stackoverflow: Meteor Unable to update package catalog. Proxy issue?

HAPI

  • JS Framework
  • Simplifies API creation with node.js
  • Developed by Wallmart
  • Good Documentation

http://hapijs.com/

Sources

Comparing Node.js Frameworks: Express, Hapi, LoopBack, Sailsjs and Meteor

Working With Data in Sails.js

Sails.js ChatApp_v0.9.3

Getting Started with LoopBack: An open source API framework written in Node.js

Intro to Meteor.js