node.jsでLambdaを実装した時のトラブル&解決策集

LambdaがVPC内で使えるようになりました！
今後、ますますLambdaの活用シーンが増えそうです。

この記事では、node.jsでLambdaを実装した時に起こるトラブルとその解決策をまとめます。
トラブル&解決策は、随時追加していきます。
こんな事で過去にはまった！というのがあれば、コメントに書いていただくか、記事を追加してプルリクして下さると嬉しいです。
トラブル&解決策を充実させて、この記事がLambdaを使う多くの人の助けになればと思います。


## トラブル１：「"errorMessage": "Process exited before completing request"」が出る

### 解決策：処理の終了を明示する

処理の終了がコード上で明示されていない時に発生します。

node.jsでファンクションを実装した場合、処理の終了を明示する必要があります。
明示には以下の3つの方法があります。

 * context.succeed(Object result);を呼び出す
    * 処理が正常に完了した事を示す
    * resultパラメータは実行結果を示すために使用する
        * 引数なし、またはnullを渡すのでもOK
 * context.fail(Error error);を呼び出す
    * 処理が正常に完了しなかった事を示す
    * errorパラメータは実行結果を示すために使用する
        * 引数なし、またはnullを渡すのでもOK
 * context.done(Error error, Object result);を呼び出す
    * errorパラメータは失敗した実行結果を示すために使用する
    * resultパラメータは成功した実行結果を示すために使用する

詳しくは[こちら](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/nodejs-prog-model-context.html)

## トラブル２：「"errorMessage": "Handler 'handler' missing on module 'index'"」が出る

### 解決策：呼び出すメソッドを正しく指定する

Lambda側の設定で、node.jsの呼び出すメソッドの指定が正しくない時に起こります。
呼び出すメソッドの指定を見直して下さい。

上記のエラーメッセージの場合だと、index.jsのhandlerメソッドを呼び出そうとしたけれど該当するメソッドが見つからない状態です。
なお、呼び出す処理はconfigurationタブの"Handler"で設定します。
下記は、index.jsのhandlerメソッドを呼び出すように設定をした例です。

![lambda1.png](https://qiita-image-store.s3.amazonaws.com/0/20223/7e0e98cd-8ba3-a0df-e059-04b2762cf8ba.png)


## トラブル３：「Cannot find module '○○○○'」が出る

### 解決策１：呼び出すjsファイルを正しく指定する

'○○○○'がLambdaの設定で呼び出す処理として指定したjsファイルの場合の解決策です。

Lambdaの設定で指定したjsファイルが見つからない時に起こります。
指定しているjsファイルが正しいか見直しをして下さい。
なお、jsファイルの指定は、node.jsのコードをパッケージでアップロードした時に有効です。
Lambdaの設定に直接node.jsのコードを書いた場合は、jsファイルの指定は何でも良いです。
（ファイルの指定に何と書いても、メソッドの指定さえ正しければ処理を呼び出せます）


### 解決策２：モジュールをパッケージに含める

'○○○○'が処理中に利用しているモジュールの場合の対策です。

必要なモジュールがアップロードしたパッケージに含まれていない時に起こります。
必要なモジュールがある場合、そのモジュールをパッケージの中に含める必要があります。
つまり、Lambdaの設定に直接node.jsのコードを書くことはできません。

## トラブル４：権限エラー「[AccessDenied: Access Denied]」が出る

### 解決策：権限設定をする

アクセス権を持たないAWSの他のリソースにアクセスしようとした時に起こります。
例えば、S3へのファイルアップロード時です。
S3のPutObjectの権限をLambdaのRoleに設定していない場合に起こります。

Lambdaの設定でS3のPutObject権限を持つRoleを設定してやります。
Roleの設定は、Configurationタブの"Role"で行います。

![lambda2.png](https://qiita-image-store.s3.amazonaws.com/0/20223/3e35c4cb-e2f6-35b5-04f3-edd8b180132f.png)

## トラブル５：一部の処理が複数回実行される

例えば、以下のコードを書いたとします。
一部省略していますが、LambdaでExcelファイルを生成して、それをS3にアップロードしています。
以下のコードだと、1回のファンクション呼び出しでS3へのアップロードが複数回行われる可能性があります。
S3側でputobjectをトリガーにしていた場合困ることがあるかもしれません。

```lang:javascript

var officegen = require('officegen');
var fs = require('fs');
var tmp = require('tmp');

var xlsx = officegen ('xlsx');

exports.handler = function(event, context){
	
	// コールバック関数の設定（Excelファイルが作られた後に呼び出される）
	xlsx.on ('finalize', function(written) {
		// ExcelファイルをS3にアップロードする
		~~
		  });
	});
	
	// Excelファイルを作る
	var sheet = xlsx.makeNewSheet();
	sheet.name = "サンプルシート";
	tmp.file(function _tempFileCreated(err, path, fd, cleanupCallback) {
		var out = fs.createWriteStream(path);
		xlsx.generate(out);
	});	
};

```


### 解決策：変数の初期化はメソッド内で行う

Lambdaでは、グローバルな変数などは再利用される事があります。

上記の例だと、変数xlsxが再利用されて、メソッドが呼び出される毎に変数xlsxに対してコールバック関数の設定が行われてしまいます。
コールバック関数は設定した分だけ処理が呼び出されてしまうので、2回設定してしまった場合、S3へのアップロード処理も2回行われてしまうのです。

変数xlsxはhandlerメソッドの中で初期化して下さい。

# お願い

冒頭にも書きましたが、Lambdaで困ったこと随時募集します。
この記事を充実させて、Lambdaで困った人を救う記事にする事を目指してます。