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
- You can only query for scalar properties
- Query results must be at the same level
- 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