Falcor 概要：もう１つのクエリー言語型 API 


# 概要

GraphQLとは別のクエリー言語型の API である [Falcor](https://netflix.github.io/falcor/) [^1]について調べました。

[^1]: falcor で検索するとネバーエンディング・ストーリーの[falkor](https://en.wikipedia.org/wiki/List_of_The_Neverending_Story_characters#Falkor_the_Luckdragon)の画像が大量に。 Phoenix / Elixir に続いてファンタジーが流行りでしょうか。

# Falcor とは

Netflix が開発したサーバーとクライアント間のデータのやりとりの仕様です。
[Facebook の GraphQL / relay](https://facebook.github.io/relay/) と同じように、RESTful な API の問題を解決するために開発されました。

REST の問題点は Facebook の GraphQL のページにまとめられています。([参考](https://facebook.github.io/react/blog/2015/05/01/graphql-introduction.html#why-invent-something-new))
Falcor でも次のような問題が解決できます。

* 複雑なデータを取ろうとすると API を何度も呼ばないといけない。
    * 例：一覧を取得後に、各要素の詳細を１つずつ取得する、など 
* 不要なデータフィールドも取得してしまう。
    * 例：name だけ欲しいのに avatar_url とかもついてくる。

# 準備

Falcor のサンプルの [falcor-express-demo](https://github.com/netflix/falcor-express-demo) を使って動きを見ながら Falcor の仕様を理解したいと思います。

サンプルを ```npm install && npm start``` し、[http://localhost:9090](http://localhost:9090) アクセスすると真っ白な画面が表示されます。
※ mac では node.js v4 では```npm i```でエラーになったので、node.js v0.12 で確認しています。

GraphQLのスキーマのように API の形が簡単にわかる方法がないようですので、[サーバーのソースコード](https://github.com/Netflix/falcor-router-demo/blob/master/index.js) を参考にしながらいろいろ試してみます。

# Path

REST では URI によってリソースを指定しますが、falcor では Path によって指定します。
Path は名前の通り、データの階層をパスを使って指定しますが、簡単なクエリーも指定できます。

サンプルコードでは ```genrelist``` というジャンルのリストを取得するパスが用意されています。

まず、ジャンルリストの１つ目を取得してみます。

* [http://localhost:9090/model.json?method=get&paths=[["genrelist",0]]](http://localhost:9090/model.json?method=get&paths=[[%22genrelist%22,0]])

```method``` には ```get```, ```set```, ```call``` が指定でき、取得は ```get``` です。
```paths``` が path で ```[[path1], [path2]]``` のように複数の path を指定できます。１つの場合は ```[[path]]``` になります。

上にアクセスすると、次のような json が取得できます。

```json:"genrelist",0
{
    "jsonGraph": {
        "genrelist": {
            "0": {
                "$type": "atom"
            }
        }
    }
}
```

```$type``` はタイプを表し、```atom``` はデータを持つオブジェクトを表します。

上記の path には取得するデータのフィールドが指定されていないため、```$type``` のみ返されました。
```genrelist``` には ```name``` があるので取得します。

* [http://localhost:9090/model.json?method=get&paths=[["genrelist",0,"name"]]](http://localhost:9090/model.json?method=get&paths=[[%22genrelist%22,0,%22name%22]])

```json:"genrelist",0,"name"
{
    "jsonGraph": {
        "genrelist": {
            "0": {
                "name": "Popular on Netflix"
            }
        }
    }
}
```

さらに ```titles``` もあるので、```titles```も取得します。

* [http://localhost:9090/model.json?method=get&paths=[["genrelist",0,["name","titles"]]]](http://localhost:9090/model.json?method=get&paths=[[%22genrelist%22,0,[%22name%22,%22titles%22]]])

```json:
{
    "jsonGraph": {
        "genrelist": {
            "0": {
                "name": "Popular on Netflix", 
                "titles": {
                    "$type": "atom"
                }
            }
        }
    }
}
```

なお、```["name","titles"]``` を２つの path に分けて([["genrelist",0,"name"],["genrelist",0,"titles"]](http://localhost:9090/model.json?method=get&paths=[[%22genrelist%22,0,%22name%22],[%22genrelist%22,0,%22titles%22]]))も結果はマージされて同じように出力されます。

次に ```0``` が指定されている部分で ```{"from":1,"to":3}``` のように指定すると複数取得することができます。

* [http://localhost:9090/model.json?method=get&paths=[["genrelist",{"from":1,"to":3},"name"]]](http://localhost:9090/model.json?method=get&paths=[[%22genrelist%22,{%22from%22:1,%22to%22:3},%22name%22]])

```json:"genrelist",{"from":1,"to":3},"name"
{
    "jsonGraph": {
        "genrelist": {
            "1": {
                "name": "Action & Adventure"
            }, 
            "2": {
                "name": "My List"
            }, 
            "3": {
                "name": "Goofy TV Shows"
            }
        }
    }
}
```

# JSON Graph

falcor から取得できるデータは json ですが、重複したデータを効率よく扱うため、独自の定義をしています。

上の例で、```$type``` に ```atom``` とありましたが、全部で３つあります。

* ```atom``` : データ
* ```ref``` : 参照
* ```error``` : エラー

参照は重複したデータをまとめるためにあります。
```genrelist``` の ```titles``` の ```name``` を取得します。

* [http://localhost:9090/model.json?method=get&paths=[["genrelist",{"from":0,"to":1},"titles",0,"name"]]](http://localhost:9090/model.json?method=get&paths=[[%22genrelist%22,{%22from%22:0,%22to%22:1},%22titles%22,0,%22name%22]])

```json:
{
    "jsonGraph": {
        "genrelist": {
            "0": {
                "titles": {
                    "0": {
                        "$type": "ref", 
                        "value": [
                            "titlesById", 
                            1
                        ]
                    }
                }
            }, 
            "1": {
                "titles": {
                    "0": {
                        "$type": "ref", 
                        "value": [
                            "titlesById", 
                            8
                        ]
                    }
                }
            }
        }, 
        "titlesById": {
            "1": {
                "name": "Curious George"
            }, 
            "8": {
                "name": "Blitz"
            }
        }
    }
}
```

```title``` は複数の genre に登録されていることがあるため、上のように参照型で返すことができます。
この構造は通信量を減らすだけでなく、クライアントでもこの構造で持つことによってメモリを効率的に使うことができます。


# 最後に

Falcor の基礎的な機能を使ってみました。
まだ、Falcor も GraphQL も十分理解できていませんが、今の印象としては

* クエリーは GraphQL の方が強力
* 取得できるデータの表現は、Falcor の JSON graph の方が強力

です。

検索してみると、すでに GraphSQL vs Falcor のようになっています。（伸びてないですが、ReactJSから[こんなポスト](https://twitter.com/reactjsnews/status/633692175335866368)も）

Facebook と Netflix が強力して組み合わせてはどうでしょうか :smile: 

# 参考

* [Falcor: One Model Everywhere](https://netflix.github.io/falcor/)
* [Falcor: Paths](https://netflix.github.io/falcor/documentation/paths.html)
* [Falcor: JSON Graph](https://netflix.github.io/falcor/documentation/jsongraph.html)

# 関連記事

* [GraphQL を試す - Qiita](http://qiita.com/yasuhiro-okada-aktsk/items/2e4d3878ed0c62688913)