node package manager
Don’t reinvent the wheel. Reuse code within your team. Create a free org »


Linen (line-in) maps API's to bindable objects, and collections. At classdojo, we use linen to abstract our API from front-end, so we don't necessarily depend on any sort of API while developing new components. This allows us to rapidly build prototypes which can be wired up later.

Here's an example person schema:

var linen = require("linen")();
  // name of the schema - gets referenced by  
  // linen.model("person") 
  name: "person",
  //fields for the person. Keep in mind that model properties 
  //get *validated* against their coresponding field.  
  fields: {
    firstName: "string",
    lastName: "string",
    // virtual value - doesn't actually exist in the API 
    fullName: {
      $get: function(model) {
        return model.get("first_name") + " " + model.get("last_name");
      $set: function(model, value) {
        var nameParts = String(value).split(" ")
        model.set("first_name", nameParts.shift());
        model.set("last_name", nameParts.join(" "));
      $bind: ["first_name", "last_name"]
    // fetches GET /people/:personId/friends when 
    // person.bind("friends").to(fn) is called 
    friends: [{
      $ref: "person",
      $fetch: function(payload, next) {
        transport.fetch(payload.method, "/people/" + payload.model.get("_id") + "/friends", next);
  //fetches GET /people/:personId when 
  //person.bind(property).to(fn) is valled 
  fetch: function(payload, next) {
    transport.fetch(payload.method, "/people/" + (payload.model.get("_id") || ""), next);

To use the schema, simply call the following:

var person  = linen.model("person"),  //creates a new person 
existingPerson = linen.model("person", "someId"); //some ID 

Linen works by overriding the bind method to fetch from any API you have setup, so when you call:

//calls GET /people/someId 
existingPerson.bind("firstName").to(function(name) {

The existingPerson will asynchronously call .to(fn) when it's been loaded from the server. This is useful when data-binding to any sort of UI component, such as rivets.js, or paperclip.js.

Here's another example of data-binding to linen models:

// GET /people/someId/friends 
existingPerson.bind("friends").to(function(friends) {
  // GET /people/friendId"firstName").once().to(function(name) {

The above examples make it easy to abstract models from any API. To demonstrate this, here's an example of using dummy data:

var existingPerson = new bindable.Object({
  firstName: "Ron",
  lastName: "Burgundy",
  friends: [
    new bindable.Object({
      firstName: "Brian",
      lastName: "Fontana"
    new bindable.Object({
      firstName: "Brick",
      lastName: "Tamland"
    new bindable.Object({
      firstName: "Champ",
      lastName: "Kind"
existingPerson.bind("firstName").to(function(name) {
  console.log(name); //Ron  
existingPerson.bind("friends").to(function(friends) {"firstName").once().to(function(name) {
    console.log(name); //Brian 



Returns a new linen instance


adds a new schema


Inspired by mongoose:

  firstName: {

linen.model(schemaName[, modelId ])

returns a new, or existing model