APIGatewayで使うLambda用の関数を作る

# 前提
Lambdaプロキシ統合を使用(LAMBDA_PROXY, Lambda Proxy)
→APIGatewayの統合リクエストで「Lambda プロキシ統合の使用」をチェック。

# テンプレート(とりあえず早く使いたい人用)
```
'use strict';

//External Libraries
//const hoge = require("hoge");

module.exports.functionname = (event, context, callback) => {
  //Respomse Template
  var response = {
    statusCode: 200,
    headers: {"Access-Control-Allow-Origin" : "*"},
    body: null
  };

  //Response.Body Template
  var body = {
    message: 'successfully!',
    input: event,
    results: null
  };

  try {
    var results = '';

    body.results = results;
    response.body = JSON.stringify(body);
    callback(null, response);
  } catch(e) {
    //Exception
    response.statusCode = 500;
    body.message = e.message;
    response.body = JSON.stringify(body);

    callback(null, response);
  } finally {

  }
};
```

# 基本的な記述(serverlessより)
## 例:handler.json
```
'use strict';

module.exports.hello = (event, context, callback) => {
  const response = {
    statusCode: 200,
    headers: {"Access-Control-Allow-Origin" : "*"},
    body: JSON.stringify({
      message: 'successfully!',
      input: event,
    }),
  };

  callback(null, response);
};
```

## レスポンスの注意
**Lambdaプロキシ統合を使用している場合、
「statusCode」
「headers」
「body」
は必須になるので注意。**

※入れないと、サーバエラーになります
※bodyの値は文字列じゃないとダメ



## LambdaでCORSを有効にするには
そもそもCORSってなによって方はこちらへ↓
[CORS簡易まとめ](http://qiita.com/tsuuuuu_san/items/870b68d99ccdc2d9cb60)

このページでは、前提として「Lambdaプロキシ統合」があるので、
レスポンスのヘッダ情報に
「"Access-Control-Allow-Origin" : "*"」
を加えるだけ。

# 解説：module.exports.関数名
まず、この記述の仕方は何か。
→この関数が、外部モジュールとして読み込まれたときの使い方を設定している。

## 深く考えたくなかったら↓
こういうものだと思って関数を定義すれば良い。

## よく知りたいなら↓
外部モジュールは

```
var hoge = require('ファイル名');
```

で読み込まれる。(Node.jsの場合)

requireの戻り値を設定するために、
呼ばれた関数では「exports」か「module.exports」で定義しなければならない。

違いは、

||説明|
|:--|:--|
|exports|オブジェクトとして返す。<br>複数のプロパティを内包させたいときに使う。|
|module.exports|オブジェクトをセットした変数？を返す。<br>クラスや単一のオブジェクトをrequireさせたいときに使う。|

# 解説：event
Lambdaが受け取るリクエストパラメータのようなもの。
Lambdaと紐付けるサービスによって、変幻自在。

API Gatewayの場合、
統合リクエストのタイプ「LAMBDA_PROXY」を使うと手早くすすめるので、
それを使いましょう。

※マッピングテンプレートでせかせか作るのはプロにまかせましょう

## よく使う情報の参照方法
### 1. クエリパラメータ

```
event.queryStringParameters.パラメータ
```

**※注意
クエリパラメータが皆無の場合、「event.queryStringParameters = null」となり、
上記参照方法も例外となる。
状況に応じては、**

```
if(event.queryStringParameters !== null && event.queryStringParameters !== undefined) {
  if(event.queryStringParameters.id !== null && event.queryStringParameters.id !== undefined) {
    id = event.queryStringParameters.id;
  }
 }
```

**↑こんなチェックも必要かも。**

### 2. POSTパラメータ

```
event.body
```

**※注意
POSTパラメータは文字列で必ず渡ってくる。
つまり、**

```
# 通常形式
id=1&name=hoge

# JSON形式
{id:1, name:'hoge'}
```

**もLambda内では一度使いやすいようにパースしてあげる必要がある。**

ゆるふわな方法は[後述](http://qiita.com/tsuuuuu_san/items/7ac256b2290ea8d14ec5#postパラメータをパースしよう)。

## (serverless)ローカルでeventに情報を渡す
1. 事前にjsonファイルで渡すデータを作っておく。

```lang:event.json
{
"queryStringParameters": {
  "id": 2
}
}
```

2.invokeするときに、作ったjsonファイルを読み込み

```
sls invoke local -f 関数名 -p jsonファイル
```

## eventの中身
```lang:eventの中身(20161125時点)
{
"resource": "/index",
"path": "/index",
"httpMethod": "GET",
"headers": null,
"queryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"requestContext": {
  "accountId": "xxxxxxxxxxxx",
  "resourceId": "xxxxxx",
  "stage": "test",
  "requestId": "xxxxxxxxxxxxxxxxxxx",
  "identity": {
    "cognitoIdentityPoolId": null,
    "accountId": "xxxxxxxxxxxx",
    "cognitoIdentityId": null,
    "caller": "xxxxxxxxxxxx",
    "apiKey": "xxxxxxxxxxxxxxxxxxx",
    "sourceIp": "xxxxxxxxxxxxxxxxxxxxx",
    "accessKey": "xxxxxxxxxxxxxxxxxxxx",
    "cognitoAuthenticationType": null,
    "cognitoAuthenticationProvider": null,
    "userArn": "arn:aws:iam::xxxxxxxxxxxx:xxxx",
    "userAgent": "Apache-HttpClient/4.5.x (Java/1.8.0_102)",
    "user": "xxxxxxxxxxxx"
  },
  "resourcePath": "/index",
  "httpMethod": "GET",
  "apiId": "xxxxxxxxxx"
},
"body": null,
"isBase64Encoded": false
}
```



# 解説：context
ランタイム情報を格納している。
使用メモリ数くらいは使えそう。

# 解説：callback
LambdaのNode4.3からサポートが追加されたもの。

ファンクションを明示的に終了するために使われる。

正直、どういう挙動をするものなのかまだよく理解できていない。

```
## 定義
callback(Error err, Object response);

## 関数実行終了
callback();

## 関数の実行に失敗
callback(err);

## 関数の実行に成功
callback(null, response);
```

|引数|説明|
|:---|:---|
|Error err|失敗時にErrorオブジェクトにその内容がセットされる|
|Object response|成功時の返却値<br>JSONにしないとだめ|

# JSON.stringify(value[, replacer[, space]])
指定した値をJSON形式に変換してくれる。

何も考えたくなければ、

```
JSON.stringify(value, null, 2);
```

が良いかと思う。(フィルタなし、半角スペース2でインデント整形)

|引数|説明|
|:---|:---|
|value|JSONに変換したいものを指定|
|replacer|指定した関数か配列でフィルタをかける<br><br>関数→引数key,valueでフィルタ処理をかける<br>配列→指定値をkeyとして、それをフィルタ(削除)|
|space|結果JSONのインデントを整えてくれる。<br><br>数値なら半角スペースで(最大10),文字列ならそれをインデント文字として使う(タブ文字なら\tなど)|

# 外部ライブラリをいれよう

```lang:mysql2モジュールを入れてみる
npm install mysql2 --save-dev
```
※node_modulesディレクトリに配置される

```lang:handler.jsonで読み込んでみる
'use strict';

const mysql = require('mysql2');

module.exports.hello = (event, context, callback) => {
～
```

# mysql2を使ってみよう
## 設定ファイルを準備
```
mkdir config && touch config/db.json
cat << EOS >> config/db.json
{
  "name": {
    "host": "localhost",
    "user": "root",
    "password": "pass",
    "database": "db_name"
  }
}
EOS
```

## 処理を記載
```lang:handler.json
'use strict';

const db = require("./config/db");
const mysql = require('mysql2');

module.exports.hello = (event, context, callback) => {
  var response = {
    statusCode: 200,
    headers: {"Access-Control-Allow-Origin" : "*"},
    body: null
  };

  var body = {
    message: 'successfully!',
    input: event,
    results: null
  };

  var con = null;

  try {
    //[1]コネクション作成
    con = mysql.createConnection(db.zpms);

    //[2]クエリ実行
    con.query('SELECT * FROM tb_test', (err, results, fields) => {
　　　//[3]結果をレスポンスに
      body.results = (err === null) ? results : err;
      response.body = JSON.stringify(body);

　　　//[4]完了
      callback(null, response);
    });

  } catch(e) {
    response.statusCode = 500;
    body.message = e.message;
    response.body = JSON.stringify(body);

    callback(null, response);
  } finally {
　　//[5]ちゃんとコネクションは閉じること
    con.end();
  }
};
```

**※con.endを明示的にしないと、Lambdaの実行が終わらない(タイムアウトまで)**

## ※プリペアードステートメントで

```
  try {
    var first = 1;
    var second = 2;

    con = mysql.createConnection(db.zpms);

    con.query('SELECT * FROM tb_test WHERE id IN (?, ?)', [first , second ], (err, results, fields) => {
      body.results = (err === null) ? results : err;
      response.body = JSON.stringify(body);

      callback(null, response);
    });

  } catch(e) {
    ~
```

# POSTパラメータをパースしよう
## バリデータとパーサーをインスコ
```
npm install validator qs --save-dev
```

参考
[validator](https://www.npmjs.com/package/validator)
[qs](https://www.npmjs.com/package/qs)

## 処理
```
const validator = require('validator');
const qs = require('qs');

~

var pParam = null;

if(event.body !== null) {
  pParam = (validator.isJSON(event.body)) ? JSON.parse(event.body) qs.parse(event.body);
}
```