What's included?


URL Routing
A simple tool for directing traffic based on URLs that are recieved both on the browser and server.

Templating
An unobtrusive templating tool that is fast, DSL free and simple to use.

Data Management
A storage agnostic resource-oriented object to document mapper for building data models with validation and sanitization.

Middleware
A Modern hybrid buffered/streaming middleware kernel backwards compatible with connect.

Plugins
A plugin manager that provides lightweight application extensibility and composition with a twist of feature reflection.

Logging
A multi-transport async logging library for node.js with plugins for many commonly targeted stores.

There's more!


Flatiron has many more repositories of useful tools! Simply explore the github repository for more details. Also, all tools are licensed under the MIT or Apache 2.0 License.



Who's responsible?


flatiron is maintained by an open source community as well it has sponsorship from nodejitsu



You can sponsor flatiron by contacting nodejitsu. You can help promote it by clicking on the tweet button. You can contribute to it by clicking on the fork button.



Philosophy

No one agrees on frameworks. It's difficult to get consensus on how much or how little a framework should do. Flatiron's approach is to package simple to use yet full featured components and let developers subtract or add what they want.

Motivation

Build a collection of decoupled, unobtrusive tools that can operate as well in unison as they do independently. Promote code organization and sustainability by clearly separating development concerns.

Getting started with Flatiron

Full project on Github

Since flatiron consists of largely a components, it's simple to start using.

Intallation

You'll need both Node.JS and NPM installed. (NPM is included by default with Node.JS, starting with version 0.6.3). You can download Node.js binary installers for MacOS and Windows here.

Flatiron comes with a commandline tool that can generate several application boilerplates.

$ [sudo] npm install flatiron -g

If everything works as expected, you'll now have a new command line tool called flatiron. To create a new project, go to an existing folder and run:

$ flatiron create <app-name> <type>

You can also simply include flatiron in your package.json and install locally with npm.

  {
    "name": "your-application",
    "dependencies": {
      "flatiron": "*"
    }
  }
Installing your dependencies with npm will install flatiron and all nested dependencies:

$ cd /path/to/your/app
$ npm install

 

URL Routing with Director

Full project on Github

What problem does it solve?

Director is a URL router. It works in a browser for single page apps and in Node.js. It's not a plugin for another framework. It's not dependent on anything. It's a modern router that was designed from the ground up with javascript.

Integration with Flatiron

A Director based router is automatically attached to your application when using flatiron.plugins.http or flatiron.plugins.cli

CLI application

  var flatiron = require('flatiron'),
       app = flatiron.app;
  
  app.use(flatiron.plugins.cli, {
    // CLI options
  });
  
  //
  // app.router is now available. app.cmd is also available
  // as a shortcut for creating routes
  //
  app.cmd('version', function () {
    console.log('flatiron ' + flatiron.version);
  });

HTTP application

  var flatiron = require('flatiron'),
      app = flatiron.app;
  
  app.use(flatiron.plugins.http, {
    // HTTP options
  });
  
  //
  // app.router is now available. app[HTTP-VERB] is also available
  // as a shortcut for creating routes
  //
  app.router.get('/version', function () {
    this.res.writeHead(200, { 'Content-Type': 'text/plain' })
    this.res.end('flatiron ' + flatiron.version);
  });
  
  app.start(8080);

How does it work?

On the client, it uses part of the URL to figure out what functions should execute, which usually has some effect on the way the page is presented.

 var routes = {
  '/authors': {
    on: showAuthor,
    '/books': { on: [showCover, showDescription] },
    '/bios': { on: [showPicture, showBiography] }
  }
};

var router = Router(routes);

On the server, it helps figure out what to do when a URL is requested from the server.

//
// require the native http module, as well as director.
//
var http = require('http'),
    director = require('director');

//
// for the sake of example, lets actually define one of the
// functions that might be called from in a routing table.
//
function showAuthor() {
  this.res.writeHead(200, { 'Content-Type': 'text/plain' })
  this.res.end('Carl Sagan');
}

var routes = {
  '/authors': {
    on: showAuthor,
    '/books': { on: [showCover, showDescription] },
    '/bios': { on: [showPicture, showBiography] }
  }
};

//
// define a routing table.
//
var router = new director.http.Router(routes);

//
// setup a server and when there is a request, dispatch the
// route that was requestd in the request object.
//
var server = http.createServer(function (req, res) {
  router.dispatch(req, res, function (err) {
    if (err) {
      res.writeHead(404);
      res.end();
    }
  });
});

//
// You can also do ad-hoc routing, similar to `journey` or `express`.
// This can be done with a string or a regexp.
//
router.get('/bonjour', helloWorld);
router.get(/hola/, helloWorld);

//
// set the server to listen on port `8080`.
//
server.listen(8080);
A route table is as simple as an object literal. A potentially nested set of key/value pairs. The keys in the object literal represent each potential part of the URL. The values in the object literal contain references to the functions that should be associated with them.  

Templating with Plates

Full project on Github

Plates (short for templates) binds data to markup. It's DSL free! Which means that there is NO special syntax. It's Javascript, Markup and JSON. It works in the browser and in node.js!

What problem does it solve?


  • DSLs (Domain Specific Languages) such as <%=foo%> or {{foo}} reduce portability.
  • DOM templating is SLOW.
  • Promote the separation of concerns principle by decoupling decision making from presentation.
  • Make both the code and markup more readable and maintainable by a wider audience.

Integration with Flatiron

No special integration with flatiron is provided, you can simply use plates by requiring it.

  var plates = require('plates');

How does it work?

By default, plates will try to match the data-key in the data to an ID in the tag, since both should be unique.

var plates = require('plates');

var html = '<div id="test">Old Value</div>';
var data = { "test": "New Value" };

var output = plates.bind(html, data); 

A common use case is to apply the new value to each tag's body based on the class attribute.

var html = '<span class="name">User</span>...<span class="name">User</span>';

var data = { "username": "John Smith" };
var map = plates.Map();

map.class('name').to('username');

console.log(plates.bind(html, data, map));

Another common case is to want to replace the value of an attribute if it is a match.

var html = '<a href="/"></a>';

var data = { "newurl": "http://www.nodejitsu.com" };
var map = plates.Map();

map.where('href').is('/').insert('newurl');

console.log(plates.bind(html, data, map));

In even more complex cases, an arbitrary attribute an be specified, if a value is matched, a specific value can be used and then used as anther attribute's value.

var html = '<a href="/"></a>';

var data = { "imageurl": "http://www.nodejitsu.com" };
var map = plates.Map();

map.where('data-foo').is('bar').use('imageurl').as('src');

console.log(plates.bind(html, data, map));
 

Data Management with Resourceful

Full project on Github

Resourceful provides an engine agnostic interface to JSON document stores, as well as a convienient and flexible caching layer.

What problem does it solve?

How often have you found yourself writing Model code in your application? Pretty often? Good! Unlike other "Object-Document Mappers" `resourceful` tries to only focus on two things:

  • A simple API for defining custom Model prototypes with validation. No special sugar is required to instantiate prototypes defined by resourceful.
  • Define an extensibility model for databases to provide CRUD functionality to Models along with custom query, filtering or updating specific to that specific implementation (Mongo, CouchDB, Redis, etc).

Integration with Flatiron

No special integration with flatiron is provided, you can simply use resourceful by requiring it.

  var resourceful = require('resourceful');

How does it work?

Resources can be defined using resourceful.define(name, factory). Once a resource prototype is created it can be treated as a normal Javascript prototype.


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

Creature.property('diet'); // Defaults to String
Creature.property('vertebrate', Boolean);
Creature.property('belly', Array);

Creature.prototype.feed = function (food) {
  this.belly.push(food);
};

var wolf = new Creature({
  diet:      'carnivor',
  vertebrate: true
});

wolf.feed('squirrel');
console.dir(wolf.belly);
// ['squirrel']
 

Middleware with Union

Full project on Github

Union is a minimalist middleware framework for node.js. It's minimalist, but extensible. Union's request handling is compatible with `connect`, meaning that all existing `connect-middlewares` should work out of the box with union.

What problem does it solve?

It provides an easy way to interact with requests before and after they are processed. But it's primary value add is that it's a hybrid buffered, streaming middleware kernel that isn't coupled with any other software.

Integration with Flatiron

By using flatiron.plugins.http you can configure and control a union server. The middleware used by union are compatible with express and connect.

  var flatiron = require('flatiron'),
      app = flatiron.app;
  
  app.use(flatiron.plugins.http, {
    //
    // List of middleware to use before dispatching 
    // to app.router
    //
    before: [],
    //
    // List of Streams to execute on the response pipeline 
    //
    after: []
  });
  
  //
  // Both app.listen(port, [host,] callback) and 
  // app.start(port, [host,] callback) are now attached 
  // to your application. app.start() will automatically 
  // call app.listen() after calling app.init().
  //
  app.listen(8000, function () {
    console.log('Application is now started on port 8000');
  });

How does it work?


//
// for the sake of this example, lets define a function to represent our
// middleware. Your middleware would likely be a more complex module.
//
function middleware1(req, res) {
  if(~req.url.indexOf('foo')) {
    return false;
  }
}

var server = union.createServer({
  before: [
    middleware1
  ]
});

router.get(/foo/, function () {
  this.res.writeHead(200, { 'Content-Type': 'text/plain' });
  this.res.end('never sent\n');
});
 

Plugins with Broadway

Full project on Github

Broadway exposes a simple "plugin" API which allows the application developer to extend the top-level application object easily.

What problem does it solve?

This plugin model translates well between the browser and the server because developers can write different plugins to suite the needs of the different environments. Broadway includes several plugins by default which can be overridden if you don't agree with the particular module or library choice we've made.

Integration with Flatiron

Each flatiron.App instance inherits from broadway.App. This means that you can write your own plugins to extend your application as you see fit.

  var flatiron = require('flatiron'),
      app = flatiron.app;

  var customPlugin = {
    name: 'custom-plugin',
    attach: function (options) {
      //
      // Extend the application
      //
      app.yourCustomMethod = function () {
        
      };
    },
    init: function (done) {
      //
      // Initialize anything your plugin needs,
      // asynchronously if necessary, then call done().
      //
      done();
    }
  };
  
  app.use(customPlugin);

How does it work?

Each plugin in broadway interacts with an application instance through four properties illustrated in the code sample below:

var plugin = {
  "name": "example-plugin", // Plugin's name

  "attach": function attach(options) {
    // Called with plugin options once plugin attached to application
    // `this` - is a reference to application
  },

  "detach": function detach() {
    // Called when plugin detached from application
    // (Only if plugin with same name was attached)
    // `this` - is a reference to application
  }

  "init": function init(callback) {
    // Called on application initialization
    // App#init(callback) called once every plugin will call `callback`
    // `this` - is a reference to application
  }
};
 

Logging with Winston

Full project on Github

A multi-transport async logging library.

What problem does it solve?

Winston is designed to be a simple and universal logging library with support for multiple transports. A transport is essentially a storage device for your logs. Each instance of a winston logger can have multiple transports configured at different levels. For example, one may want error logs to be stored in a persistent remote location (like a database), but all logs output to the console or a local file.

There also seemed to be a lot of logging libraries out there that coupled their implementation of logging (i.e. how the logs are stored / indexed) to the API that they exposed to the programmer. This library aims to decouple those parts of the process to make it more flexible and extensible.

Integration with Flatiron

Using flatiron.plugins.log will automatically extend your application with logging capabilities from winston. This plugin is included by default in both CLI and HTTP flatiron apps.

  var flatiron = require('flatiron'),
      app = flatiron.app;

  app.log.info('Logging to my flatiron application', {
    with: 'custom metadata'
  });

How does it work?


var winston = require('winston');

winston.log('info', 'Hello distributed log files!');
winston.info('Hello again distributed logs');

By default, only the Console transport is set on the default logger. You can add or remove transports via the add() and remove() methods:

winston.add(winston.transports.File, { filename: 'somefile.log' });
winston.remove(winston.transports.Console);