Netflix Falcorについて（３）

[Netflix Falcorについて（１）](http://qiita.com/sand/items/704dc924235b131b437c "Netflix Falcorについて（１）")
[Netflix Falcorについて（２）](http://qiita.com/sand/items/7c062a6e25352ad6949a "Netflix Falcorについて（２）")
[Netflix Falcorについて（３）](http://qiita.com/sand/items/02838dac3308a35cb51d "Netflix Falcorについて（３）")
[Netflix Falcorについて（４）](http://qiita.com/sand/items/9b581b82b6fe40cd1989 "Netflix Falcorについて（４）")
[Netflix Falcorについて（５）](http://qiita.com/sand/items/a2f764462b9c19092baf "Netflix Falcorについて（５）")




　今回はサーバ側でfalor-routerを使ってDataSourceを実装してみます。まずはサーバ側の設定を以下の手順で行います。

```sh:インストール
mkdir falcor-qiita3
cd falcor-qiita3
npm init

npm install express --save
npm install falcor-express --save
npm install falcor-router --save
npm install mongoose --save

mongoimport --db qiita3db --collection articles --jsonArray initData.js --host=127.0.0.1

node index.js
```

　initData.jsはMongoDBの初期データで以下のように２つのcollectionを含みます。ブログのエントリーとでも考えてください。


```js:initData.js
[
  {
    title: '夏休み１日目',
    content: '１日目は東武動物公園のプールに行った'
  },
  {
    title: '夏休み２日目',
    content: '２日目は森林公園に行ってセミをとった'
  }
]

```

　目指すのはクライアント側から Virtual JSON のデータ要求があった時に、サーバ側で、Falcor Routerがその Virtual JSON を創り出し返すことです。今回の例は、Virtual性が低いのですが、後に同じ枠組みでVirtual性の高いものにしていくつもりです。サーバ側のコードは以下のようです。Falcor Routerで２個のrouteを定義しています。


```js:index.js
var falcorExpress = require('falcor-express');
var Router = require('falcor-router');

var express = require('express');
var app = express();

//---------- 以下はMongoDB & Mongooseのお決まりの設定です
var mongoose = require('mongoose');
var databaseUri = 'mongodb://localhost/qiita3db';
mongoose.Promise = global.Promise;
mongoose.connect(databaseUri, { useMongoClient: true })
const articleSchema = {
  title:String,
  content:String
};

const Article = mongoose.model('Article', articleSchema, 'articles');
//----------


// expressにおいてfalcor-routerを使ってDataSourceを実装しています
app.use('/model.json', falcorExpress.dataSourceRoute(function (req, res) {
  // Routerのハンドラーで Virtual JSON resourceを作りreturnします
  return new Router([

    // １個目のroute定義
    {
      route: 'articles.length',
      get: () => { return  Article.count({}, (err, count) => count)
        .then ((articlesCount) => {
          return {
            path: ['articles', 'length'],
            value: articlesCount
          };
        })
      }
    },

    // ２個目のroute定義
    {
      route: 'articles[{integers}]["_id","title","content"]',
      get: (pathSet) => {
        const index = pathSet[1];

        return Article.find({}, (err, docs) => docs)
          .then ((docsArray) => {
            var results = [];

            index.forEach((index) => {
              const singleObject = docsArray[index].toObject();
              const result = {
                path: ['articles', index],
                value: singleObject
              };
              results.push(result);
            });
            return results;
          });
      }
    }

  ]);
}));

// serve static files from current directory
app.use(express.static(__dirname + '/'));

var server = app.listen(3000);
```

　それぞれのrouteに対応する要求をクライアント側から発行します。クライアント側のコードは以下のようになります。サーバ側の１個目のroute定義とクライアント側の１個目のパスが対応します。２個目のroute定義と２個目のパスが対応します。サーバ側はクライアントの要求に応じた結果(JSON Graphの一部)を創り出し、returnします。ここで注意ですが、routeのgetハンドラーはPromise( or Observable)をreturnします。例えば２個目のrouteハンドラーは「return Article.find({}, ...」とreturnしていて、MongooseのPromiseを返しています。（ただし非同期処理を含まない場合は静的な値をreturnして良い）。クライアント側でどのようなパスを発行して、サーバ側でどのようなJSON Graphを創り出すかは、プログラマが設計する必要があります。仕様が決まれば、クライアント側のコーディングは格段に楽になります。


```index.html
<html>
  <head>
    <!-- Do _not_  rely on this URL in production. Use only during development.  -->
    <script src="//netflix.github.io/falcor/build/falcor.browser.js"></script>
    <script>
      var model = new falcor.Model({source: new falcor.HttpDataSource('/model.json') });

      var length=0;
      async function fetch() {
        await model. // １個目のパス
          get("articles.length").
          then(function(response) {
            var res = JSON.stringify(response,null,2);
            console.log(res);
            document.write("<div>"+res+"</div>");
            length = response.json.articles.length;
          });

        await model. // ２個目のパス
          get(['articles', {from: 0, to: length-1}, ['_id','title', 'content']]).
          then(function(articlesResponse) {
            var res2 = JSON.stringify(articlesResponse,null,2);
            console.log(res2);
            document.write("<div>"+res2+"</div>");
          });
      }

      fetch();

    </script>
  </head>
  <body>
  </body>
</html>
```

　ブラウザでindex.htmlを開くと、以下のような実行結果がブラウザに表示されます。望みの結果をサーバ側から取得できました。

```ブラウザ上の表示
{ "json": { "articles": { "length": 2 } } }
{ "json": { "articles": { "0": { "_id": "5986bd440720b6845e72238c", "title": "夏休み１日目", "content": "１日目は東武動物公園のプールに行った" }, "1": { "_id": "5986bd440720b6845e72238d", "title": "夏休み２日目", "content": "２日目は森林公園に行ってセミをとった" } } } }
```

　クライアント側の説明です。model.get()で２度modelにアクセスしていますが、最初のgetの結果(length)を２回目のget()で使用するので、同期をとって実行される必要があります。そのためここでは async/await を使っています。

　最初のget()のパスは"articles.length"でarticleオブジェクトのlengthプロパティにアクセスしています。２番目のget()のパスは少し複雑です。

```js:
get(['articles', {from: 0, to: length-1}, ['_id','title', 'content']])
```
　articles配列の、indexが0～length-1の要素の、それぞれ'_id','title', 'content'プロパティにアクセスすることを意味しています。このパスの指定はfalcorに特徴的なものです。取得するindexの範囲指定とプロパティの集合を明示的に指定します。必要以上の量のデータを取得するのを避けるためです。例えればSQLでselect文には必ずwhere句を付けるようにと、公式サイトでは説明されています。（この制限を避けるためには$atomを使う）

　またgetメソッドはPromise(or Observable)を返しますので注意してください。thenで受けることもできますし、subscribeで受けることもできます。公式サイトではsubscribeの使用を勧めているようです。

　今回の例は、JSON GraphのGraph性が感じられないものですが、次回以降にJSON Graphの説明に入っていきたいと思います。

　最後にpackage.jsonを掲載しておきます。

```package.json
{
  "name": "falcor-qiita3",
  "version": "1.0.0",
  "description": "mkdir falcor-qiita3\r cd falcor-qiita3\r npm init",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.15.3",
    "falcor-express": "^0.1.4",
    "falcor-router": "^0.8.1",
    "mongoose": "^4.11.5"
  }
}
```