Introduction

to

Falcor

Netflix

Alternative to REST

Today's World

"REST" APIs

http://localhost/api/user

http://localhost/api/user?offset=1&limit=25&order_by="name"

http://localhost/api/user?offset=1&limit=25&order_by="name"&include_friends=true&include_pets=true

A single endpoint evolves as consumers needs change

What if we could request properties instead of resources?


{
    id: 1,
    name: "Leanne Graham",
    username: "Bret",
    email: "Sincere@april.biz",
    address: {
        street: "Kulas Light",
        suite: "Apt. 556",
        city: "Gwenborough",
        zipcode: "92998-3874",
        geo: {
            lat: "-37.3159",
            lng: "81.1496"
        }
    },
    phone: "1-770-736-8031 x56442",
    website: "hildegard.org",
    company: {
        name: "Romaguera-Crona",
        catchPhrase: "Multi-layered neural-net",
        bs: "harness real-time e-markets"
    }
}

JSON

JSON

var model = 
{
  id: 1,
  name: "Leanne Graham",
  username: "Bret",
  email: "Sincere@april.biz",
  address: {
    street: "Kulas Light",
    suite: "Apt. 556",
    city: "Gwenborough",
    zipcode: "92998-3874",
    geo: {
      lat: "-37.3159",
      lng: "81.1496"
    }
  },
  phone: "1-770-736-8031 x56442",
  website: "hildegard.org",
  company: {
    name: "Romaguera-Crona",
    catchPhrase: "Multi-layered neural-net",
    bs: "harness real-time e-markets"
  }
}
const log = msg => x => {
  console.log(msg, x)
  return x
}


function log2 (msg) {
  return function(x) {
    console.log(msg, x)
    return x
  }
}   


log('name: ')(model.name)
// "name: Leanne Graham"

How do I get to the name property?

Paths

var model = new falcor.Model({
cache: {
  user: {
    id: 1,
    name: "Leanne Graham",
    username: "Bret",
    email: "Sincere@april.biz",
    address: {
      street: "Kulas Light",
      suite: "Apt. 556",
      city: "Gwenborough",
      zipcode: "92998-3874",
      geo: {
        lat: "-37.3159",
        lng: "81.1496"
      }
    },
    phone: "1-770-736-8031 x56442",
    website: "hildegard.org",
    company: {
      name: "Romaguera-Crona",
      catchPhrase: "Multi-layered neural-net",
      bs: "harness real-time e-markets"
    }
  }
}})
model
  .getValue('user.name')
  .then(log('name: '))

model
  .getValue('user.name')
  .then(x => log('name: ')(x))

// "name: Leanne Graham"

How do I get to the name property?

JSON

var model = {
users: [
  {
    id: 1,
    name: "Leanne Graham",
    username: "Bret",
    email: "Sincere@april.biz",
    address: {
      street: "Kulas Light",
      suite: "Apt. 556",
      city: "Gwenborough",
      zipcode: "92998-3874",
      geo: {
        lat: "-37.3159",
        lng: "81.1496"
      }
    },
    phone: "1-770-736-8031 x56442",
    website: "hildegard.org",
    company: {
      name: "Romaguera-Crona",
      catchPhrase: "Multi-layered neural-net",
      bs: "harness real-time e-markets"
    }
  }, 
  { ... }, 
  { ... }
  ]
}
log('name: ')(model.users[0].name)
// "name: Leanne Graham"

How do I get to the name property of the first element in users?

Paths

var model = new falcor.Model({
cache: {
  users: [
    {
      id: 1,
      name: "Leanne Graham",
      username: "Bret",
      email: "Sincere@april.biz",
      address: {
        street: "Kulas Light",
        suite: "Apt. 556",
        city: "Gwenborough",
        zipcode: "92998-3874",
        geo: {
          lat: "-37.3159",
          lng: "81.1496"
        }
      },
      phone: "1-770-736-8031 x56442",
      website: "hildegard.org",
      company: {
        name: "Romaguera-Crona",
        catchPhrase: "Multi-layered neural-net",
        bs: "harness real-time e-markets"
      }
    }, 
    { ... }, 
    { ... }
    ]
  }
}
model
  .getValue('users[0].name')
  .then(log('name: '))

//=> "name: Leanne Graham"

How do I get to the name property of the first element in users?

Paths

var model = new falcor.Model({
cache: {
  users: [
    {
      id: 1,
      name: "Leanne Graham",
      username: "Bret",
      email: "Sincere@april.biz",
      address: {
        street: "Kulas Light",
        suite: "Apt. 556",
        city: "Gwenborough",
        zipcode: "92998-3874",
        geo: {
          lat: "-37.3159",
          lng: "81.1496"
        }
      },
      phone: "1-770-736-8031 x56442",
      website: "hildegard.org",
      company: {
        name: "Romaguera-Crona",
        catchPhrase: "Multi-layered neural-net",
        bs: "harness real-time e-markets"
      }
    }, 
    { ... }, 
    { ... }
    ]
  }
}
stringify =
    JSON.stringify

model
  .get('users[0].["name","email"]')
  .then(stringify)
  .then(log('res : '))

//=> "res: {
  "json": {
    "users": { 
      "0": { 
        "name": "Leanne Graham", 
        "email": "Sincere@april.biz"
      } 
    }
  }
}

How about name and email?

Paths

var model = new falcor.Model({
cache: {
  users: [
    {
      id: 1,
      name: "Leanne Graham",
      username: "Bret",
      email: "Sincere@april.biz",
      address: {
        street: "Kulas Light",
        suite: "Apt. 556",
        city: "Gwenborough",
        zipcode: "92998-3874",
        geo: {
          lat: "-37.3159",
          lng: "81.1496"
        }
      },
      phone: "1-770-736-8031 x56442",
      website: "hildegard.org",
      company: {
        name: "Romaguera-Crona",
        catchPhrase: "Multi-layered neural-net",
        bs: "harness real-time e-markets"
      }
    }, 
    { ... }, 
    { ... }
    ]
  }
}
model
  .get('users[0..1].["name","email"]')
  .then(stringify)
  .then(log('res : '))

//=> "res: {
  "json": {
    "users": { 
      "0": { 
        "name": "Leanne Graham", 
        "email": "Sincere@april.biz"
      },
      "1": {
        "name": "Ervin Howell",
        "email": "Shanna@melissa.tv"
      }
    }
  }
}

How about name and email for users 0 and 1?

Paths

var model = new falcor.Model({
cache: {
  users: [
    {
      id: 1,
      name: "Leanne Graham",
      username: "Bret",
      email: "Sincere@april.biz",
      address: {
        street: "Kulas Light",
        suite: "Apt. 556",
        city: "Gwenborough",
        zipcode: "92998-3874",
        geo: {
          lat: "-37.3159",
          lng: "81.1496"
        }
      },
      phone: "1-770-736-8031 x56442",
      website: "hildegard.org",
      company: {
        name: "Romaguera-Crona",
        catchPhrase: "Multi-layered neural-net",
        bs: "harness real-time e-markets"
      }
    }, 
    { ... }, 
    { ... }
    ]
  }
}
model
  .setValue('users[0].phone', '555-555-5555')
  .then(log('res : '))

//=> "res: '555-555-5555'

How about setting user 0's phone to "555-555-5555"?

Paths

var model = new falcor.Model({
cache: {
  users: [
    {
      id: 1,
      name: "Leanne Graham",
      username: "Bret",
      email: "Sincere@april.biz",
      address: {
        street: "Kulas Light",
        suite: "Apt. 556",
        city: "Gwenborough",
        zipcode: "92998-3874",
        geo: {
          lat: "-37.3159",
          lng: "81.1496"
        }
      },
      phone: "1-770-736-8031 x56442",
      website: "hildegard.org",
      company: {
        name: "Romaguera-Crona",
        catchPhrase: "Multi-layered neural-net",
        bs: "harness real-time e-markets"
      }
    }, 
    { ... }, 
    { ... }
    ]
  }
}
var data = {
  json: {
    users: {
      0: { 
        name: 'Barb',
        username: 'Barb'
      }
    }
  }
}

model
  .set(data)
  .then(stringify)
  .then(log('res : '))

//=> "res: {
  json: {
    users: {
      0: { 
        name: 'Barb',
        username: 'Barb'
      }
    }
  }
}

How about setting user 0's name and username to "Barb"?

Paths

var model = new falcor.Model({
cache: {
  users: [
    {
      id: 1,
      name: "Leanne Graham",
      username: "Bret",
      email: "Sincere@april.biz",
      address: {
        street: "Kulas Light",
        suite: "Apt. 556",
        city: "Gwenborough",
        zipcode: "92998-3874",
        geo: {
          lat: "-37.3159",
          lng: "81.1496"
        }
      },
      phone: "1-770-736-8031 x56442",
      website: "hildegard.org",
      company: {
        name: "Romaguera-Crona",
        catchPhrase: "Multi-layered neural-net",
        bs: "harness real-time e-markets"
      }
    }, 
    { ... }, 
    { ... }
    ]
  }
}
var data = {
  name: 'John Doe',
  username: 'jdoe',
  email: 'jdoe@email.com',
  ...
}
 
model
  .call('users.add', data)  
  .then(log('res : '))

How do I create a user?

Path Syntax String -> PathSet

'user.name' === ['user','name']

'user["name","email"]' === ['user',['name','email']]

'users[0]["name","email"]' === ['users',0,['name','email']]

'users[0..2]["name","email"]' === ['users',{'from':0,'to':2},['name','email']]

Falcor Rules

  1. You can only query for scalar properties
  2. Query results must be at the same level
  3. You cannot query for whole arrays or objects unless it is an atom

 

JSON Graph

ref, atom, error

Ref

{ $type: "ref", value: ["usersById", 2}

Ref

{
  "jsonGraph": {
    "users": {
      "0": {
        "$type": "ref",
        "value": [
          "usersById",
          1
        ]
      }
    },
    "usersById": {
      "1": {
        "email": "Sincere@april.biz",
        "name": "Leanne Graham"
      }
    }
  }
}

Atom

{ $type: "atom", value: ['en', 'fr'] }

Atom

{
  "jsonGraph": {
    "users": {
      "0": {
        "$type": "ref",
        "value": [
          "usersById",
          1
        ]
      }
    },
    "usersById": {
      "1": {
        "geo": {
          "$type": "atom",
          "value: {
             lat: "-37.3159",
             lng: "81.1496"
          }
        }
      }
    }
  }
}

Error

{ type: "error",  value: "The request timed out." }

Error

{
  "jsonGraph": {
    "users": {
      "0": {
        "$type": "ref",
        "value": [
          "usersById",
          1
        ]
      }
    },
    "usersById": {
      "1": {
        "geo": {
          "$type": "error",
          "value: "The request timed out."
        }
      }
    }
  }
}

Intermission

Bonus

Caching

Batching

var mdl = new falcor.Model()

var model = mdl.batch()

RX Observable

const onNext = log('onNext: ')

const onError = log('onError: ')

const onComplete = log('onComplete: ')

model
  .get(['users',0,['name','email']])
  .subscribe(onNext, onError, onCompleted)

Central State

Demo

Server Overview

Introduction to Falcor

By cgoboncan_ebsi

Introduction to Falcor

  • 931