Nodejitsu

Save time managing and deploying your node.js app. Code faster with jitsu and npm

Resource Reflection in Flatiron

About the author

Name
Location
Worldwide
nodejitsu nodejitsu

In computer science, reflection is the ability of a computer program to examine and modify the structure and behavior (specifically the values, meta-data, properties and functions) of an object at runtime - Wikipedia


The Flatiron framework is designed to help with the rapid development of robust isomorphic JavaScript applications. Flatiron is built on the node.js philosophy of small, distinct, usable components. Every component of the framework can be used independently, for it's own distinct purpose.

Resource-View-Presenter

While developers have the choice to assemble these Flatiron components in anyway they like, Flatiron encourages the use of the RVP (Resource-View-Presenter) pattern.

Using the RVP pattern allows developers to move their applications towards a domain driven design. This rather utopian computer science idea is usually muddled by several layers of UML diagrams and poorly instrumented tool chains.

Flatiron's approach towards attaining such programming nirvana is a bit more simple and realistic.

Resources are JavaScript Objects

A Resource is a JavaScript object. You've probably worked with them before.

Let's define a Creature resource using the resourceful library.

var resourceful = require('resourceful');  
var Creature  = resourceful.define('creature');  

Creature is a constructor function for creating new Creatures. It's easy I promise.

var bob = new Creature({  
  id   : 'Bob',
  type : 'dragon'
});

You can totally still export Creature in a Node.js module.

exports.Creature = Creature;  

This newly created Creature instance Bob comes with some built-in methods to help you out with performing basic CRUD operations.

By default, resources will use an in-memory storage engine provided by resourceful. The storage engine itself is pluggable and supports several modern database engines.

//
// save, create, destroy, all, find, etc...
//
bob.save({ type: "giant moth" }, function(err, result){

});

Here are several code examples of using these CRUD methods.

Woah, I don't want this CRUD

Easy! You can, noop them out, override their definitions at will, or just igore them. A resource is a just a JavaScript object and create, save, etc are just functions!

over-ride built-in resource methods

// No more delete
delete Creature.destroy;

// Custom save
Create.save = function(id, data, callback){  
  callback(null, 'The googles do nothing');
});

Extending Bob the Dragon

Now we have a new Creature, Bob the Dragon. Bob can pretty much do anything a respectful CRUD dragon could do.

Well, almost! We are also going to add a feed action to Creatures, because Bob will probably get hungry at some point.

Creature.feed = function (_id, options, callback) {  
  var self = this;
  self.get(_id, function(err, creature){
    if(err) {
      return callback(err);
    }
    var life = creature.life + 1;
    self.update(_id, { life: life }, function(err, result){
      callback(null, 'I have been fed my life is: ' + result.life);
    });
  });
  self.remote = true;
}

Note: The remote property is used to indicate the method should be reflected via public remote interfaces. Any methods which are not remote, will not be exposed to public interfaces.*

Let's get reflecting

I thought this was a post about reflection? Well it is!

  • We defined our resource, Creature
  • We gave Creature a custom feed method
  • We created Bob the Dragon

Now we are going to reflect our Creature across across several interfaces!

Flatiron currently supports resource reflection across:

simple restful server:

restful.createServer([Creature]).listen(8000, function () {  
  console.log(' > restful server started on port 8000');
});

individual code examples can be found in each reflection library

This means that Bob the Dragon can be expressed as a fully functional HTTP / websocket isomorphic application with a command line interface.

This sounds like a giant clusterf*ck of auto-generated code

No way! First off, there is zero auto-generated or scaffolded code in the reflection process. I've done that before, and it's stupid.

Flatiron breaks down the problem of uber-reflection into separate and distinct modules that have no direct dependency or relation to each other.

The core dependency ( or the R in RVP) is the resourceful library. Resourceful allows you to define resources. The Flatiron framework then has several distinct projects for reflecting resources across well-defined and well tested interfaces.

Customization already sounds impossibly complicated

No way! Any interface not directly reflected can just as easily be implemented using the appropriate backing library. All Flatiron modules are broken down into distinct and re-usable projects.

For example, if you used restful to reflect a RESTful router server-side in node.js, you will then have access to a Director.router instance. This router is created by the heavily used Director library.

If you need to override a generated route, or create an ad-hoc route, or make any customization, the API is exactly the same as the Director API.

customize a reflected router interface:

app.router.get('/', function(){  
  this.res.end('home page');
});

//
// Overrides `/creature/larry` but won't override,
// any other `/creature/:id` captures.
//
app.router.get('/creatures/larry', function(){  
  this.res.end('larry is special!');
});

Like most of Flatiron's reflection libraries, restful is built to solve 90% of use-cases. If you hit a case where restful is causing a problem, you can simply drop into the Director API.

Resource reflection is highly encouraged, but most definitely optional.

TL;DR; All of Flatiron's reflection libraries are optional and have a well-defined library backing it's interface.

Wait, what about schema? What about validations?

Resourceful provides support for custom validation functions and an optional JSON-Schema implementation through revalidator.

You can arbitrarily create validation functions or use JSON-Schema on any property or resource and all interfaces will respect these validations at run-time. It's slightly magical.

validation on individual properties

// Number between 0 and 10
Creature.property("life", Number, { min: 0 max: 10 });

// Email validation
Creature.property("email", String, { format: 'email' });

// Custom validation conforms
Creature.property("even", Number, {  
  conform: function (value) {
    return value % 2 === 0;
  } 
});

use before and after hooks for resource validations on resource actions

Creature.before('create', function(options, callback){  
  if(options.foo !== 'bar') {
    return callback(new Error('foo is not bar!'));
  }
  console.log('about to create a new creature!', options);
  callback(null, options);
});

Creature.after('create', function(err, result){  
  console.log('Creature was created!', result);
});


Fine, this might work. What about relational data?

Easy! Resourceful supports relationships between resources ( one-one, one-many, many-many ). Relationships are defined in resources through the Resource.parent API.

Each reflection library will respect these relationships and act accordingly. You can find additional code examples for setting up relational data in resourceful here or in the README for the specific reflection library you are using.

defining a simple resource relationship

var Album = resourceful.define('album');  
var Song  = resourceful.define('song');  
Song.parent('album');  

Everything else will be handled for you by Flatiron. It's that easy.

Okay, I believe. What now?

Get over to http://github.com/flatiron and get started!

Flatiron is already the most contributed to Node.js Framework on Github. With the upcoming 1.0 version, we are about to enter a new age of isomorphic JavaScript frameworks and web-development. Join the JavaScript party!

Giddy-up!