Win a Raspberry PI 3

Agenda

  • GraphQL, by François De Campredon (@FdeCampredon) - durée 25 min 
  • FalcorJS, by Mathieu Breton (@MatBreton) - duration 25 min
$: 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 !

Why

An idea coming from Netflix

 

 

REST, JSON-RPC, ...

The current ways to build an API is not sufficient

State of art

REST 

+ 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"
    }
}

State of art

JSON-RPC

-  One route = one view

-  Hard to cache

+ Lightweight

/users?id=1?props=id,name

 

{
    id: "1",
    name: "Mathieu Breton"
}

State of art

JSON is not perfect

/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 ..."
}

So why ?

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.

What

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

JSON Graph

Represents a graph in JSON

Use symbolic link

One model everywere

JSON Graph

{
    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)

Path

Walk through the JSON Graph

todos[0].name
["todos", 0, "name"]

PathSet

todos[0..4]["name","done"]
["todos", { from: 0, to: 4 }, ["name","done"]]

DataSource

Used to retrieve and update data in the Json Graph

  • HttpDataSource
  • Router (server only)
  • ModelDataSource

DataSource API

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

DataSource API

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));

Router - DataSource

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
                }
            ]);
        }
    }
]);

Model

Wrap datasource, used by the Front-End to access to data.

 

Responsible of caching

Model example

const model = new falcor.Model({
    source: new falcor.HttpDataSource('/model.json')
});

model.getValue('todos[0].name').then(console.log);

How

Let's start with a simple example.

Simple client side 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);
  • ​Promise
  • Async
  • Node callback style
  • Node stream

Simple client side example

const model = new falcor.Model({
  source: new falcor.HttpDataSource("/model.json")
});

// prints "Underwood" eventually
model.getValue("user.surname").
  then(console.log);

(with loading)

Over the network

/model.json?paths=["user.surname"]

GET /model.json?paths=["user.surname"]
{
  user: {
    surname: "Underwood"
  }
}

Server side simple example

// 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);

Server side simple example

// 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]
                    }))
            );
        }
    }
]);

Conclusion

Netflix give a way to load data :

  • Fine grained

  • Easy to Batch

  • Easy to Cache

  • The same access everywhere

  • Resawing

Thank you

&

Questions