$: whoami
Mathieu Breton
CTO at @jsrepublic
-oy/`
`/yNMdyNMdo-
-odMmy/` `-odNNy/`
`/ymNdo-` .+yNNh+-
-+hNmy+.` `:sdNms:`
`:smNds:` `.+hmNh+-
:hNmh+.` `:sdNms
yMh` `..`` `:MM`
yMy +mmmmm. -odmmNNmmdy+ .MM`
yMy oMMMMM-/NMMMMNMMMMMo .MM`
yMy oMMMMM-mMMMMy---/os` .MM`
yMy oMMMMM-dMMMMNdho/- .MM`
yMy oMMMMM-.hNMMMMMMMNd/ .MM`
yMy oMMMMM- -+shmNMMMMM+ .MM`
yMy oMMMMM- .-.``.-dMMMMh .MM`
yMy .:-:mMMMMN``NMNmdddNMMMN/ .MM`
yMy hMMMMMMMN+ omNMMMMMMNNh: `yy`
yMy `ohmmmmds- .-://///:.
yMh.` ```
:ymNho-`
`:odNmy/`
.+hNNh+.`
`:smNds:`
-+hNNh+. `+s`
`/yNMdhMNy-
-oy:
<= We hire !
An idea coming from Netflix
The current ways to build an API is not sufficient
+ Easy to cache
+ Uncoupled
- Fat
/users/1 [GET]
{ id: "1", name: "Mathieu Breton", job: "CTO", company: { id: "2" name: "Js-Republic", address : "11 Rue de Rome, Paris" } }
- One route = one view
- Hard to cache
+ Lightweight
/users?id=1?props=id,name
{ id: "1", name: "Mathieu Breton" }
/users/1
{ id: "1", name: "Mathieu Breton", job: "CTO", company: { id: "2" name: "Js-Republic", address : "11 ..." } }
/companies/2
{ id: "2" name: "Js-Republic", address: "11 ..." }
Find a solution to get the data from a backend, with an uncoupled, lightweight and easy to cache architecture.
And ... Have a model more closed to the reality. In other word, a graph.
Falcor is a library which presents the data from backend like a JSON Graph in the Front side. It deals for us the loading, caching, bashing.
And give a way to define routes to serialize on demand.
Version : 0.1.17
Creation Date : April 2015
The data is the API
Represents a graph in JSON
Use symbolic link
One model everywere
{
todosById: {
"44": {
name: "get milk from corner store",
done: false,
prerequisites: [{ $type: "ref", value: ["todosById", 54] }]
},
"54": {
name: "withdraw money from ATM",
done: false,
prerequisites: []
}
},
todos: [
{ $type: "ref", value: ["todosById", 44] },
{ $type: "ref", value: ["todosById", 54] }
]
};
Symbolic link (aka Ref)
Walk through the JSON Graph
todos[0].name
["todos", 0, "name"]
todos[0..4]["name","done"]
["todos", { from: 0, to: 4 }, ["name","done"]]
Used to retrieve and update data in the Json Graph
Set & Get
const response = dataSource.get([
["todos", 0, ["name", "done"]],
["todos", 0, "prerequisites", { from: 0, to: 1 }, ["name", "done"]]
]);
const response = dataSource.set({
paths: [ ["todos", 0, "prerequisities", { to:1 }, "done"] ],
jsonGraph: {
todos: {
0: {
prerequisites: {
1: {
done: true
}
}
}
}
}
});
Idempotent
Idempotent
Call
datasource.
call(
// the callPath
["todos", "remove"],
// the args array containing the id
// of the todo to remove
[42],
[],
// retrieve the length of the list after the function has completed
[
["length"]
]).
subscribe((jsonGraphEnvelope) => console.log(jsonGraphEnvelope));
Used in server side, it maps path to data
const dataSource = new Router([
{
route: 'todos.push',
call(callPath, args) {
const todoId = args[0];
return todosService.removeTodos(todosId)
.then((todoIdAndLength) => [
{
path: ['todos', {from: todoId, to: todoIdAndLength.length}],
invalidated: true
},
{
path: ['todos', 'length'],
value: titleIdAndLength.length
}
]);
}
}
]);
Wrap datasource, used by the Front-End to access to data.
Responsible of caching
const model = new falcor.Model({
source: new falcor.HttpDataSource('/model.json')
});
model.getValue('todos[0].name').then(console.log);
Let's start with a simple example.
const model = new falcor.Model({
cache: {
user: {
name: "Frank",
surname: "Underwood",
address: "1600 Pennsylvania Avenue, Washington, DC"
}
}
});
// prints "Underwood" eventually
model.getValue("user.surname").
then(console.log);
const model = new falcor.Model({
source: new falcor.HttpDataSource("/model.json")
});
// prints "Underwood" eventually
model.getValue("user.surname").
then(console.log);
(with loading)
/model.json?paths=["user.surname"]
GET /model.json?paths=["user.surname"]
{
user: {
surname: "Underwood"
}
}
// app.js
const express = require('express');
const falcorMiddleware = require('falcor-express');
const TODORouter = require('./todo-router');
const app = express();
// Create a new Router instance for each new request
app.use('/model.json', falcorMiddleware.dataSourceRoute((req, res) =>
new TODORouter;
));
const server = app.listen(80);
// todo-router.js
const router = new Router([
{
route: "user['name','surname','address']",
get(pathSet) {
return userService.loadUser().then((user) =>
pathSet[1].map((userKey) => ({
path: ["user", userKey],
value: user[userKey]
}))
);
}
}
]);
Netflix give a way to load data :
Fine grained
Easy to Batch
Easy to Cache
The same access everywhere
Resawing
&