APIGateway+Lambdaでイカしたエラーレスポンスをする

## 経緯

APIGateway+Lambdaでこんな感じのイカしたエラーレスポンスをしたい。
http://qiita.com/suin/items/f7ac4de914e9f3f35884#github

```json
{
   "message": "Validation Failed",
   "errors": [
   ]
}
```

でも意外と苦労したのでメモ。

## Lambdaのレスポンスに困った

Lambdaのcallbackで  `cb(err, null)` をコールするとerrの型がなんであれ、

```json
{
  "errorMessage": "何かのエラー",
  "errorType": "Error",
  "stackTrace": [
    "module.exports (/var/task/src/operation/xxxx.js:27:9)",
    "next (native)",
    "onFulfilled (/var/task/node_modules/co/index.js:65:19)",
    "process._tickDomainCallback (node.js:407:9)"
  ]
}
```

* errorMessage
* errorType
* stackTrace

という3属性をもったオブジェクトを返します。というかこの３つの属性以外返ってきません。

なので、

```js
class HogeError extends Error {
    constructor(msg, detail) {
       super(msg);
       this.detail = detail;
    }
}
```

こんな独自Errorを定義しても`detail`属性は無視されてしまいます。

## どうすれば良いのか

### 1. Lambda側(Node.js側)でJSON.stringifyする

```js
class HogeError extends Error {
  constructor(msg, details) {
    const obj = {
      code: "Forbidden",
      message: msg,
      errors: details
    };
    super(JSON.stringify(obj));   //<---ここでmessageに文字列化されたJSONをセットする
  }
}
```

こうするとLambdaのレスポンスはこんな感じになります。

```json
{
  "errorMessage": "{\"code\":\"Forbidden\",\"message\":\"hogehoge\",\"errors\":[{\"reason1\":\"hogeがあれなんでだめ\"},{\"reason2\":\"これがhogeなんでだめ\"}]}",
  "errorType": "Error",
  "stackTrace": [...]
}
```

### 2. APIGateway側でレスポンスのマッピングテンプレートを使う

```velocity:マッピングテンプレート
#set ($e = $util.parseJson($input.path('$.errorMessage')))
{
  "message" : $e.message,
  "errors": $e.errors
}
```

このようにして、errorMessageの文字列をparseJsonし、マッピングする設定をします。
See: [developerguide/api-gateway-mapping-template-reference](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html#util-template-reference)

### 3. これで晴れて次のようなレスポンスを返すことができました。

```json:ステータス403
{
   "message" : hogehoge,
   "errors": [{reason1=hogeがあれなんでだめ}, {reason2=これがhogeなんでだめ}]
}
```

## 蛇足

### 応答コードを決める

応答コードを出し分けるところでも若干悩んだので書いておきます。
統合レスポンスの設定で、正規表現マッチングをして応答コードを決められるのですが、これが何に対するマッチングなのかというところです。

![responseintegrate.png](https://qiita-image-store.s3.amazonaws.com/0/31010/67c5382d-a6ec-a567-6130-9c0a0b9ecfd1.png)


[developerguide/how-to-method-settings-execution-console](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/how-to-method-settings-execution-console.html)によると、

> Note
エラーパターンは Lambda 応答の errorMessage プロパティに対してマッチングされます。これは、Node.js の場合は context.fail(errorMessage)、Java の場合は throw new MyException(errorMessage) によって設定されます。

ということで、先の例で行くと、

```json
{
  "errorMessage": "{\"code\":\"Forbidden\",\"message\":\"hogehoge\",\"errors\":[{\"reason1\":\"hogeがあれなんでだめ\"},{\"reason2\":\"これがhogeなんでだめ\"}]}",
  "errorType": "Error",
  "stackTrace": [...]
}
```

errorMessageの値、つまり、`{"code":"Forbidden","message":...` という文字列に対するマッチングとなります。

### swaggarでの定義書式

エクスポートした奴の抜粋を晒しておきます。

```yaml:swagger.yamlの抜粋
x-amazon-apigateway-integration:
  responses:
    default:
      statusCode: "200"
    .*"code":"Forbidden".*:
      statusCode: "403"
      responseTemplates: 
        application/json: |
          #set ($e = $util.parseJson($input.path('$.errorMessage')))
          {
            "message" : $e.message,
            "errors": $e.errors
          }
    .*"code":"BadRequest".*:
      statusCode: "400"
      responseTemplates:
        application/json: |
          #set ($e = $util.parseJson($input.path('$.errorMessage')))
          {
            "message" : $e.message,
            "errors": $e.errors
          }

```

### さくっと試せるサンプル

https://github.com/NewGyu/api-gateway-serverless

ここに置いておきます。[serverless framework](https://github.com/serverless/serverless)でデプロイできるようにしてあります。

```shell-session
$ sls deploy
```

が、上記の統合レスポンスの設定を`serverless.yml`でどう書いて良いのやらわからず、swagger書式のモノの抜粋を示した次第であります。

(バージョンが上がって、`s-function.json`とか`s-templates.json`とかはなくなっちゃったんでしょうか？？）