LoginSignup
28
20

More than 3 years have passed since last update.

API GatewayからLambdaを呼び出すときにevent.bodyをオブジェクトで受け取る

Last updated at Posted at 2021-01-07

はじめに

AWS API GatewayからLambdaを呼び出すこと、よくあると思います。
Lambdaではevent.bodyでリクエストボディを取得できますが、
デフォルトではこのbodyは文字列なので、
Content-Type:application/jsonの場合も「JSON文字列」になります。
そのため、一度JSONをパースしてあげないといけません。

たとえばNode.jsの場合はこんな感じ。

index.js
exports.handler = async (event) => {
    const body = JSON.parse(event.body);
    console.log('api received!', body);

    const response = {
        statusCode: 200,
        body: JSON.stringify([body.hoge, body.fuga, body.piyo]),
    };
    return response;
};

コード側でJSON.parse()すること自体は大した手間ではないのですが、
面倒くさいのがテストです。

Lambdaの「テストイベントの設定」で、こんな感じで書きたいわけですよ。

{
  "body": {
    "hoge": 1,
    "fuga": 2,
    "piyo": 3
  }
}

しかしながら、これは「JSON文字列」ではなく「JSONオブジェクト」なので、
API Gatewayのevent.bodyとは互換性がありません。
こうしてやらなければならない。。。

{
  "body": "{\"hoge\":1,\"fuga\":2,\"piyo\":3}"
}

image.png

これはねぇ、、、辛いですよね。
ダブルクォートのエスケープの嵐で、サクッと手書きで書くことは無理です。

解決方法

API Gatewayの「マッピングテンプレート」という機能を使うと、
ちゃんとJSONをオブジェクトにしてからLambdaに飛ばすことができます。

Lambda側のトリガー設定から新規REST APIを作成

  • 検証用に、ごくごくシンプルな設定でAPIを作成
    • 実運用では「オープン」は危険なので要注意
  • HTTP APIではマッピングテンプレートが使えないので、REST APIにする

image.png

API Gatewayのリソース設定

  • API Gatewayのリソース設定に飛んぶ
  • 「ANY」メソッドの「統合リクエスト」を開く

image.png

  • Lambdaから作ったAPIは「Lambdaプロキシ統合の使用」がチェックされているので、このチェックを外す。
    image.png

  • チェックを外すとすると詳細な設定項目が現れるので、「マッピングテンプレート」を開く
    image.png

マッピングテンプレートの設定

  • リクエスト本文のパススルー:「テンプレートが定義されていない場合 (推奨)」を選択
  • マッピングテンプレートの追加:「application/json」を入力
  • テンプレートの生成:「メソッドリクエストのパススルー」を選択すると、いい感じのプリセットが入る
    • 書式が独特なので、一から書くのが大変なのですよね。。このプリセットがとってもありがたい。

image.png

  • ほとんどプリセットのままで良いのだけど、body-jsonはcamelCaseのbodyJsonにしておくと、JavaScriptでは扱いやすい
    • もしくはシンプルにbodyでもOK
  • 編集したら「保存」

 ##  This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload
 #set($allParams = $input.params())
 {
-"body-json" : $input.json('$'),
+"bodyJson" : $input.json('$'),
 "params" : {
 #foreach($type in $allParams.keySet())
     #set($params = $allParams.get($type))

APIのデプロイ

  • アクションから「APIのデプロイ」を選択
  • ステージ「default」を選んで、説明は書きたければ書いて、「デプロイ」

image.png
image.png

Lambdaの修正版と実行結果

「Lambdaプロキシ統合の使用」が外れたことで、
レスポンスの作り方も変わります。
statusCodeはAPI Gateway側で定義するので、
Lambdaではシンプルに、正常系のレスポンスをそのままreturnするだけ。

index.js
exports.handler = async (event) => {
    const body = event.bodyJson;
    console.log('api received!', body);

    return [body.hoge, body.fuga, body.piyo];
};

Lambdaテストイベント

{
  "bodyJson": {
    "hoge": 1,
    "fuga": 2,
    "piyo": 3
  }
}

PCターミナルからcurlでAPI叩いてテスト

$ curl -X POST https://example.amazonaws.com/default/mappingTemplateTest -H 'Content-Type:application/json' -d '{"hoge":1,"fuga":2,"piyo":3}'
[1,2,3]                                                  

いい感じですね!

参考サイト

AWS公式
* REST API のデータ変換の設定
* API Gateway で Lambda プロキシ統合を設定する

ブログ・Qiita
* [AWS]API Gatewayの本文マッピングテンプレートを理解する
* API Gateway + Lambda にFormからPOSTする時のマッピングテンプレートを作成しました
* API Gatewayのマッピングテンプレートの設定例

ではまた~。

28
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
20