ニフクラmBaaSお役立ちブログ

スマホアプリ開発にニフクラmBaaS。アプリ開発に役立つ情報をおとどけ!

スクリプトでmBaaSを拡張してみよう

mBaaSで提供されているスクリプトを">スクリプト機能を使えばサーバサイドにロジックを持たせて処理できるようになります。難点として、利用できる言語がJavaScriptしかないということがあります。JavaScriptは書ける人は多い言語ですが、実際に使えるレベルで書けるかどうかは別問題だったりします。

今回はスクリプト機能を使う基礎を紹介します。

基本形

まずはスクリプトの基本形を紹介します。

module.exports = function(req, res) {
  // ここにスクリプトを記述
  res.status(200).json({message: "Hello"});
};

module.exports に対して関数(function)を渡すのが基本です。その際、req/resという2つの引数が渡ってきます。前者がリクエストで、スクリプトが実行された時に渡ってくる情報です。例えばユーザエージェント、クエリーストリング、フォームで指定したユーザの入力データなどになります。

後者のレスポンスはスクリプトの中では最後に使う程度でしょう。res.status(200) が基本で、処理が成功した場合には 200 または 201 を返します。200は一般的な処理成功、201はPOSTメソッドでのデータ作成完了時に使われることが多いです。そして、その後 json メソッドなどを使ってデータを返します。特に json である必要はありませんが、プログラムへの引き渡しを考えるJSONが便利でしょう。

この形についてはおまじないとして覚えてしまうのが良いかと思います。

非同期処理について

JavaScriptではファイルやネットワーク処理などが非同期で行われます。そうしないとメインプロセスが結果を待って固まってしまうからです。特にスクリプト機能ではネットワーク機能が使われることでしょう。

スクリプト機能では superagent というネットワークライブラリが提供されていますので、これを使って外部サーバとのやり取りを行います。

var request = require('superagent');
request
  .get('http://localhost:4001/')
  .end(function(err, res){
    assert(200 == res.status);
    assert('tobi' == res.text);
    next();
  });

request.post('/user')
  .set('Content-Type', 'application/json')
  .send('{"name":"tj","pet":"tobi"}')
  .end(callback)

このようにヘッダー、ボディ、URLを指定してGETであれば get、 POSTであれば post メソッドを実行します。この時、処理結果を end メソッドを使って非同期に受け取るのがJavaScriptの書き方の特徴と言えます。

その結果、ネットワークを一度経由するとネストが一つ深くなってしまいます。

request
  .get('http://localhost:4001/')
  .end(function(err, res){
    // 別なアクセス
    request
      .get('http://localhost:4001/')
      .end(function(err, res){
        assert(200 == res.status);
        assert('tobi' == res.text);
        next();
      });
  });

あまり深くしないための方法としては、実行結果を関数で受け取るようにすれば良いでしょう。

request.post('/user')
  .set('Content-Type', 'application/json')
  .send('{"name":"tj","pet":"tobi"}')
  .end(callback)

function callback(err, res) {
  // 結果の処理
}

非同期処理部分をうまく書ければ見通しの良いコードになります。逆にネストをどんどん使っていくととてもメンテナンスしづらいコードになってしまうでしょう。

非同期とループ処理は相性が悪い

データを一つずつ取り出して処理する際にループ処理が使われます。

for (var i = 0; i < count; i++) {
  // 何かの処理
}

その処理において非同期処理を絡めると次のようになります。多くの場合、ループごとに変わる変わる変数を持っているでしょう。

for (var i = 0; i < count; i++) {
  superagent()
    .then(function() {
      // ネットワーク処理成功時
    })
}

多くの場合、これではうまく動きません。ネットワークへのアクセスが発生する前にループ処理によって変数が上書きされてしまい、同じ内容で連続的にアクセスが発生するためです。

回避策として、例えば関数としてラッピングしてしまうと言う方法があります。

function httpAccess(url) {
  superagent()
    .then(function() {
      // ネットワーク処理成功時
    })
}
for (var i = 0; i < count; i++) {
  var url = "https://example.com/i=" + i;
  httpAccess(url);
}

このように変数のスコープを切り離すことで誤った上書きを防げるようになります。ただし、この場合でもネットワークへのアクセス順番は保証されません。アクセスしたタイミングによっては遅延が発生するからです。

非同期処理を完全なループ処理にするのは非常に面倒であり、あまりお勧めしません。非同期処理部分についてはアクセス順番は保証されないと考えた方が良いかもしれません。


JavaScriptはWebブラウザで使われたきただけに「書ける」人は多い言語です。ただし、正しく動くJavaScriptを書くのは意外と大変だったりします。スクリプト機能ではサーバサイドJavaScriptになりますので、その特性を理解しつつ記述してください。