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

ボクココ

サービス開発を成功させるまでの歩み

AWS Lambda の Node.js で連続で外部APIを叩く作法

AWS JavaScript

ども、@kimihom です。

今回は AWS Lambda における Node.js のコードの書き方について。

f:id:cevid_cpp:20160404205358p:plain

実装したいこと

例えば、id を複数持った配列があるとして、その配列を 一個一個 HTTP リクエストで叩きたい、ということがあるだろう。id単位でしかリソースを削除できないような API があった場合などは必ずそんな場面に出くわす。

これを AWS Lambda で実装するには、どうすれば良いだろうか。ここに AWS Lambda の落とし穴が潜んでいる。何も考えずに書くとこんな感じになるだろう。

var request = require('request');

exports.handler = function(event, context) {
  var host = "https://api.awesomeapp.com";
  var ids = [1,2,3,4];
  ids.forEach(function(val) {
    request.delete({
      uri: host + "/records/" + id,
      json: true
    }, function(err, response) {
      context.done(); //?
    });
  });
};

これだと当然、期待した動作にならないのはわかるだろう。 AWS Lambda は、最後まで処理を終えた時点で context.done() を呼ばなければならず、これだと処理の途中で呼ばれてしまう。じゃあと言って、コールバック地獄の Node.js コードを書いてしまったら負けだ。非同期前提の JavaScript で頭を悩ますことになる。さて、どうすべきか。

Async を使う

AWS Lambda のドキュメントにもあるが、Async を使ってこの問題を解消させよう。最初は特殊な書き方に手こずるかもしれないが、慣れればなんとかなる。

今回は、 async.mapSeries を利用してこの問題を解決する。

var request = require('request');
var async = require('async');

exports.handler = function(event, context) {
  var host = "https://api.awesomeapp.com";
  var ids = [1,2,3,4];

  async.mapSeries(ids, function(id, callback) {
    request.delete({
      uri: host + "/records/" + id,
      json: true
    }, callback);
  }, function(err, res) {
    context.done();
  });
};

mapSeriesは第一引数に連続処理させたいアイテムを指定する。そんで第二引数の function の id にそれぞれが渡るようになっている。 function の中で callback を非同期に呼ぶような処理を書き、すべての ids の配列ループが終わったら、 第三引数の function が呼ばれる。

このように書けば、すっきりと連続した 外部 API のリクエスト処理を AWS Lambda で動かすことができる!

あと たいていの Node.js ライブラリは、 コールバック引数に (error, response) の順番で入ってくることを意識しよう。この慣習を守ることで、 Async の処理をより簡潔に記述することができる。上記コードの callback もその仕組みを利用して省略して書くことができている。より冗長的に書くと、以下のように書くことも可能である。

    request.delete({
      uri: host + "/records/" + id,
      json: true
    },  function(err, res) {
        callback(err, res);
    });

async には、mapSeries の他に、シンプルなコールバック地獄を防ぐための waterfall も用意されている。 AWS Lambda を Node.js で扱うには、これらのメソッドを使いこなし、すべての非同期処理が完全に終わったタイミングで、 context.done() を呼ばなければならない。Asyncにはその他様々なコールバック操作の仕組みが用意されている。詳しくはドキュメントを参照していただきたい。

終わりに

普段 Node.js をあまり書かない人にとって AWS Lambda を使いこなすには、 Async のような非同期処理のライブラリの知識が必須とも言える。私も普段は Ruby でプログラムを書いているし、Webフロントエンドの JavaScript ではあまり複雑に非同期処理を書いたりしないため、なかなか慣れずに苦労した。本記事が AWS Lambda で Node.js を扱う方にとって有益な記事になれば幸いだ。