読者です 読者をやめる 読者になる 読者になる

lxyuma BLOG

開発関係のメモ

Rest経由のparseを使うBackbone.Viewを自作する

parse.comはiOS/android/Backbone.js等の各clientが準備されているが、

Restの口も準備されているので、実質、どの言語からも叩く事が出来る。

公式pageの内容をまとめて試してみて、

最後にBackboneのViewに組み込んでPluginにしてみた。

そこまでのメモ

メリデメ

先にメリデメまとめると、

  • メリット
    • とにかく気軽(Backend全く準備が要らない)
    • 1ヶ月100万requestまでは無料
  • デメリット
    • 少し気になる程、遅い。(後述)
    • Parse固有の事がある(relation等)ので、これで書いてしまうと、ベンダーロックされる
    • queryがmongoDB丸だしなので、これも書いてしまうと、mongoDBから抜け出すのが大変

いずれにせよ、単純なテーブル構成でとりあえず作って見た、的なもの

ハッカソンや個人制作アプリやモック制作等。

で良ければ、これで十分。

※勿論、parseをproductで使う予定があるのであれば、そのまま作って行けば何も問題は無い。プロダクト事例もいっぱいあるしね。

http header

  • requestボディはjson形式 (Content-Typeはapplication/json)

  • ヘッダにApplicationIdとRestApiKeyが必要

    • それぞれparse.com上の設定画面からコピペしておく
X-Parse-Application-Id: Your App Id

X-Parse-REST-API-Key: Your REST API Key 

※urlのhostにBasic認証のID/パスワード書く形でも上記のIDとKeyを書けるみたい。

URL + HTTPメソッド

endpointのapi.parse.comに対して、以下のURLを組み立てる

http://api.parse.com/<version>/classes/<className>/<objectId>
  • 上記の< >=自分で記述するparamater

    • version
      • 今とりあえず、1でいいみたい。
    • className
      • Parse側で保存したいリソース名。テーブル名。要するにmongoDBのコレクション名。
    • objectId
      • Parseで管理している識別子。要するに、mongoDBのid。ある特定のリソースを指定したい時に、記述する。(次のHTTPメソッドに詳しく書く)
  • HTTP メソッド

    • 新規作成:POST
      • objectId付けない。
    • 取得:GET
      • 全件取得はobjectId付けない。
      • 1件取得はobjectId付ける。
    • 更新:PUT
      • objectId付けて、何を更新するか指定が必要。
    • 削除:DELETE
      • objecjtId付けて、何を削除するか指定が必要。

URL+httpメソッドの例

例えば、tasksというクラスに対するCRUD操作は以下になる。

# Create
    POST http://api.parse.com/1/classes/tasks/
# Read(全件取得)
    GET http://api.parse.com/1/classes/tasks/
# Read(1件取得)
    GET http://api.parse.com/1/classes/tasks/abCDefGHij
# Update
    PUT http://api.parse.com/1/classes/tasks/abCDefGHij
# Delete
    DELETE http://api.parse.com/1/classes/tasks/abCDefGHij

curlで試す

curlで試してみる

新規作成

curl -X POST \
  -H "X-Parse-Application-Id: YourApplicationId" \
  -H "X-Parse-REST-API-Key: YourRestApiKey" \
  -H "Content-Type: application/json" \
  -d '{"score":1337,"playerName":"Sean Plott","cheatMode":false}' \
  https://api.parse.com/1/classes/GameScore
  • もし、存在しないclassName指定すると、新規にclass自体を作ってくれる
    • 但し、その時、結構、遅い(今やったら8secかかった)
    • 既にあるclassNameであれば、1,2sec程(それでも遅いですね)

取得

  • 以下で全件取得してみる
curl -X GET \
  -H "X-Parse-Application-Id: YourApplicationId" \
  -H "X-Parse-REST-API-Key: YourRestApiKey" \
  https://api.parse.com/1/classes/GameScore/
  • 例えば、2件同じデータを入れてみたときの出力結果
{"results":[
    {"score":1337,
    "playerName":"Sean Plott",
    "cheatMode":false,
    "createdAt":"2013-12-13T13:24:07.037Z",
    "updatedAt":"2013-12-13T13:24:07.037Z",
    "objectId":"ilCXeuD0Iv"},

    {"score":1337,
    "playerName":"Sean Plott",
    "cheatMode":false,
    "createdAt":"2013-12-13T13:24:32.211Z",
    "updatedAt":"2013-12-13T13:24:32.211Z",
    "objectId":"io5sO9G2gQ"}]}
  • 1件取得したければ、上記のobjectIdをurlの最後のpathに入れる

更新

  • 上記の一番上のデータを更新してみる
curl -X PUT \
  -H "X-Parse-Application-Id: YourApplicationId" \
  -H "X-Parse-REST-API-Key: YourRestApiKey" \
  -H "Content-Type: application/json" \
  -d '{"score":73453}' \
  https://api.parse.com/1/classes/GameScore/ilCXeuD0Iv
  • うーん時間かかる(2sec程)
    • 更新をした後で、改めて、GETで、取得してみると、更新されてる。
  • なんか、CRUDで更新が一番、時間かかるっぽい
    • 何度も更新していくと、落ち着いてくるので、mongodbのinplace updateはみ出て、padding factor調整してるっぽい動き

BackboneでRestのparseを使う

ここからが、本題。

やりたい事

Parse.comでBackbone.js用のClientがあるのに、これがイマイチ。現実的に使えない。

やりたいのは、単純にBackbone.syncのバックエンドとしてparse使えればいいだけなのに。

parse付属のBackboneクライアントの何がイマイチなのか?

古いBackboneをforkして、かなり独自に実装してしまってる

  • Backbone.Objectって?
    • 中見るとBackbone.Modelから大分違うソースになってる
    • validation周りの動きが変。
  • Backbone.Syncすら無い。
    • イベントsyncとかrequestとか動きが変(無い)

結論、これに別のプラグインとか突っ込んで行くと色々問題があって、結局使えない。

多分、あえて独自実装にして、ベンダーロックインさせたいんだろうけど、

出来れば、Backbone本体+αでどうにかしたい。

そうしたら、他のpluginも使えるし。

Backbone.Model内でどうにかする

という事で書いてみた

var Task = Backbone.Model.extend({
  urlRoot: "https://api.parse.com/1/classes/tasks"
});

var myTask = new Task();
myTask.set({"name": "jikken"});

myTask.save({}, {
  headers:{
    'X-Parse-Application-Id': ‘YourAppId’,
    'X-Parse-REST-API-Key': ‘YourRestAPIKey’ }});

parseのRESTの使い方に沿って、単にヘッダー追加しただけ。

なお、ssl入れないと通らないので注意。

plugin化

毎回、こんなView書いて行ったら大変なので、

これをpluginとして作って見た。

https://github.com/lxyuma/backbone-parse-sync

Backbone.Viewを拡張してる。

使い方

githubに書いた通りだけど、

  • parseHeadersにid/keyをセット
  • Model/CollectionのuseParse:trueにすればいいだけ。
<script src="dist/backbone-parse-sync.min.js"></script>
<script>
  // settings
  window.parseHeaders = {
    ApplicationId: "*****************************",
    RESTAPIKey: "*****************************"
  };

  // Model
  TestModel = Backbone.Model.extend({
    useParse: true,
    urlRoot: "/blogs" // this means using parse 'blogs' object
  });
  testObject = new TestModel();
  testObject.set({'name': 'test'});
  testObject.save();
</script>

これで、各CRUD処理書くと、parseに対してRESTしてCRUDする。

本体ソース

短いので、貼付けるが、前に書いたソースと大体同じ様な感じで、

  • ヘッダー追加してsyncして
  • parseする時に、json調整してる
        sync: function(){
            if (this.useParse === true) {
                _.extend(_.last(arguments), {
                    url: "https://api.parse.com/" + this.parseVersion + "/classes/" + _.result(this, "url"),
                    headers: {
                        'X-Parse-Application-Id': window.parseHeaders.ApplicationId,
                        'X-Parse-REST-API-Key'  : window.parseHeaders.RESTAPIKey}
                });
            }
            return Backbone.sync.apply(this, arguments);
        },
        parse: function(resp, options) {
            if (this.useParse === true && _.has(resp, "results")){
                return resp.results;
            } else {
                return resp;
            }
        }

おしまい

まあ、クライアント側にkey/id貼付けてるのも気になるので、

お遊びや検証/学習やモック作成やハッカソン等で

上記plugin等使えればいいかと。