BFT名古屋 TECH BLOG

日々の業務で得た知識を所属するエンジニアたちがアウトプットしていきます。

【AWS API Gateway, Lambda】プロキシ統合を使うとき/使わないときの値のやりとり

AWS,APIGateway,Lambda,プロキシ統合

はじめに

こんにちは~、BFT名古屋支店の猫です。
毎年秋になると"焼き芋", "安納芋"などと書かれたお菓子を買いあさるのですが、今年はカントリーマアムの紅天使味にドハマりしています。みなさん見かけたら買うべし。

前回の「RESTful API 作ってみた!」という記事で、AWSAPI Gateway、Lambda、RDS を使ったRESTful API の作り方をご紹介しました。
今回は、この中でも特に苦労したプロキシ統合の使い方についてまとめていきたいと思います。

  • API GatewayからLambdaへの値の受け渡し方法を知りたい
  • プロキシ統合?マッピングテンプレート?頭の中が「?」だらけなんですが?

という方の参考になればと思います。

▼前回の記事はこちら

bftnagoya.hateblo.jp

プロキシ統合とは

API GatewayからLambdaを呼び出す際、デフォルトではリクエストパラメータの受け渡しは行われません。
Lambda側でリクエストパラメータを知りたい場合、2つの実装方法があります。

1つめはAPI Gatewayマッピングテンプレートというものを設定する方法です。

API Gateway マッピングテンプレートの設定画面

マッピングテンプレートでは、リクエストからどのパラメータを抽出し何という変数名でLambdaに渡すかを定義します。Lambdaに送る情報を制限することができる点はメリットですが、API GatewayとLambdaそれぞれで実装する必要があるため実装や管理が煩雑になります。

2つめの方法、プロキシ統合ではこのデメリットを解消することができます。
プロキシ統合とは「API Gatewayマッピングをしなくても、リクエストに入っているパラメータをまるっとLambdaに渡してくれる機能」です。 リクエストパラメータを自動でいい感じに変数に詰めてLambdaに渡してくれるため、実装や管理の煩雑さが軽減されます。
デメリットとしては、Lambdaに送る情報を制限することができない点が挙げられます。

上記で2つの方法をご紹介しましたが、そもそもリクエストからパラメータを抽出する必要がない場合はどちらも不要です。

つまり、リクエストパラメータの受け渡しという観点でみると、

  • リクエストからパラメータを抽出する必要がない場合
    ⇒ プロキシ統合を使用しない(マッピングテンプレートも使用しない)
  • リクエストからパラメータを抽出する必要があり、その処理をAPI Gatewayで実装したい場合
    ⇒ プロキシ統合を使用しない(マッピングテンプレートを使用する)
  • リクエストからパラメータを抽出する必要があり、その処理をLambdaで実装したい場合
    ⇒ プロキシ統合を使用する

と考えればよさそうです。

プロキシ統合使うとき/使わないときの値のやり取り

ここでは実際にプロキシ統合を使うとき / 使わないときの、API Gateway・Lambdaの設定値やコードについて記載します。
以下のリクエスト例のように、リクエストからパスパラメータbodyを抽出したい場合を想定しています。

  • リクエスト例
    • URL
      https://example.execute-api.ap-northeast-1.amazonaws.com/my-recipe-prod/{id}
      例)
      https://example.execute-api.ap-northeast-1.amazonaws.com/my-recipe-prod/3
    • メソッド:PATCH
    • body の中身
  {
    "title": "チキンカレー",
    "making_time": "45分",
    "serves": "4人",
    "ingredients": "トマト,玉ねぎ,肉,スパイス",
    "cost": "1000"
  }

プロキシ統合使わないとき

リクエストからパラメータを抽出する処理をAPI Gatewayで実装したい場合、API Gatewayで「プロキシ統合の使用」を設定せずにLambda統合を使用することで実現できます(非プロキシ統合やカスタム統合とも呼ばれます)。

この場合、リクエスト内の取り出したいパラメータをAPI Gatewayマッピングテンプレートで指定する必要があります。 マッピングテンプレートの書き方については、以下サイトの「$input 変数」「$input 変数テンプレートの例」部分を参照してください。

docs.aws.amazon.com

  • API Gateway の設定
    • 統合タイプ:Lambda関数
    • Lambda プロキシ統合の使用:□
    • マッピングテンプレート
      • リクエスト本文のパススルー:テンプレートが定義されていない場合(推奨)
        • Content-Type:application/json (中身は下記の通り)
          ※パスパラメータである「id」と、「body」を抽出してLambdaに連携する設定をしています。
        {
          "id" : "$input.params('id')",
          "body" : $input.json('$')
        }
  • Lambda の中身
import json

def lambda_handler(event, context):

  '''
  入力受け取りの書き方
  # API Gatewayで定義したパラメータがevent変数の中に詰められて渡ってくる
  '''  
  req_id = int(event['id'])  # そのまま取り出すと文字列型になってしまうので、数値として使用する場合はint型に変換する
  req = event['body']    # dict形で受け取れる

  '''
  レスポンスの書き方
  # レスポンスbodyを返してあげればよい
  '''  
  responce = {
                "message": "Success!",
                "req_id": req_id
              }

  return responce

プロキシ統合使うとき

リクエストからパラメータを抽出する処理をLambda内で実装したい場合、API Gatewayで「プロキシ統合の使用」を設定することで実現できます。

統合プロキシを使用する場合、レスポンスの書式がある程度決まっているので注意が必要です。
詳しくは以下サイトの「プロキシ統合のための Lambda 関数の出力形式」部分を参照してください。
docs.aws.amazon.com

  • API Gateway の設定

    • 統合タイプ:Lambda関数
    • Lambda プロキシ統合の使用:■
  • Lambda の中身

import json

def lambda_handler(event, context):
  
  '''
  入力受け取りの書き方
  # 全てのパラメータがevent変数の中に詰められて入ってくる
  '''  
  req_id = int(event['pathParameters']['id'])  # そのまま取り出すと文字列型になってしまうので、数値として使用する場合はint型に変換する
  req = json.loads(event['body'])  # json.loadsを用いることでdict型で読み込める


  '''
  レスポンスの書き方
  # ステータスコードやヘッダーも付けて返す必要がある
  '''  
  responce = {
               "isBase64Encoded": True,
               "statusCode": 200,
               "headers": {"MyHeader": "MyHeaderVal"},
               "body": "my body"
              }

  return responce

※注意:body部分にjson形式でデータを入れたい場合
body部分に直接dict形式を挿入するとエラーがでます。エスケープ処理が必要です。

  '''
  レスポンスの書き方
  '''  
  res_body = {
              "message": "Success!",
              "id": req_id
              }

  responce = {
               "isBase64Encoded": True,
               "statusCode": 200,
               "headers": {"MyHeader": "MyHeaderVal"},
               "body": json.dumps(res_body)  # エスケープ処理
              }

  return responce

おわりに

こうしてまとめると、なにをドタバタ大騒ぎしていたのか?と思うくらいの内容ですが 、分からないときって本当に何も分からないんですよね~(遠い目)
この記事を読んでいただいた方はすんなり実装できるといいなと思います。

今回は同じAPIの中でメソッドごとにプロキシ統合を使ったり使わなかったりといった実装にしてしまったため、引数の型が違ったりレスポンスの返し方が違ったりしてあんまりきれいじゃないコードになってしまいました。

他の人からの見やすさ、コードの移植性などを考えると、設計段階でどちらかに統一したほうがよさそうですね。

ここまで読んでいただきありがとうございました^^

参考

docs.aws.amazon.com

docs.aws.amazon.com

dev.classmethod.jp

qiita.com

cres-tech.hatenablog.com

maitakeramen.hatenablog.com