はじめに

こんにちは!
CI事業部ソリューション開発セクションの古賀です。

AWS内のデータを取り出す際に、以下の構成を使用することは多いと思います。
Lambdaの内容によっては、Apache VTL(Velocity Template Language)を使用して
Lambdaを置き換えることが可能です。

今回は、クエリ文字列とAPI Gatewayのマッピングテンプレートを使用して
動的にAPIのレスポンスを変更してみます。

今回実施すること

今回の目的は、投げたクエリ文字列に対してレスポンスを受け取ることです。その目的を達成するために、以下の構成を作成します。統合先として、DummyJSONが提供するHTTPエンドポイントを使用します。

なお、DummyJSONはJSONレスポンスを返すエンドポイントとして使用しているだけであり
他の統合先を指定しても同様のことを実現可能です。

Apache VTL(Velocity Template Language)

Apache VTLはAPI Gatewayのマッピングテンプレートや
AppSyncのリゾルバーテンプレートを書く際に使用する言語です。
VTLを使用すればデータの検証や加工ができます。

基本的なApache VTLの書き方

変数は’$’で参照します。
変数の設定には#setを使用します。

## コメントは##で始めます。
#set($name = "World")
Hello, $name!

条件文は#if、#elseif、#else

#if($age >= 18)
 You are an adult.
#else
 You are a junior.
#end

ループは #foreach を使います。

#set($list = ["Apple", "Banana", "Cherry"])
#foreach($item in $list)
 $item
#end

※条件文とループは#endで閉じる

マッピングテンプレートの作法

マッピングテンプレートでは以下の事前に定義された変数があります。
$input:リクエストで渡されたパラメータとレスポンスを取得できます。
$context:リクエストのコンテキスト情報を取得できます。
$util:マッピングテンプレートで使用できる関数が用意されてます。

詳しくは以下の公式ドキュメントをご確認ください。
API Gateway マッピングテンプレートとアクセスのログ記録の変数リファレンス

クエリ文字列を取得してみる

URL:https://xxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev?property=kogaa
APIのURLに’property’というキーで’kogaa’という値を設定します。

統合レスポンスのマッピングテンプレートに以下を入力します。

マッピングテンプレート
application/json

#set($allParams = $input.params())
{
 #foreach($type in $allParams.keySet())
  #set($params = $allParams.get($type))
  #foreach($paramName in $params.keySet())
   #if($paramName == "property")
    #set($queryString = $params.get($paramName))
   #end
  #end
 #end
 #if($queryString)
  "property":"$queryString"
 #end
}

$input.paramsで取得したインプットの内容から
キーが’property’のものを$queryStringに設定して出力します。

出力結果

{
 "property":"kogaa"
}

クエリ文字列を使ってレスポンスを変える

統合先にはDummyJSONが提供するHTTPエンドポイントを指定します。
マッピングテンプレートなしでのレスポンスは以下のようになっています。

{
 "id": 1,
 "title": "Essence Mascara Lash Princess",
 "description": "The Essence Mascara Lash Princess is a popular mascara known for its volumizing and lengthening effects. Achieve dramatic lashes with this long-lasting and cruelty-free formula.",
 "category": "beauty",
 "price": 9.99,
 "discountPercentage": 7.17,
 "rating": 4.94,
 "stock": 5,
 "tags": [
  "beauty",
  "mascara"
 ],
 "brand": "Essence",
 "sku": "RCH45Q1A",
 "weight": 2,
 "dimensions": {
  "width": 23.17,
  "height": 14.43,
  "depth": 28.01
 },
 "warrantyInformation": "1 month warranty",
 "shippingInformation": "Ships in 1 month",
 "availabilityStatus": "Low Stock",
 "reviews": [
  {
   " rating": 2,
   "comment": "Very unhappy with my purchase!",
   "date": "2024-05-23T08:56:21.618Z",
   "reviewerName": "John Doe",
   "reviewerEmail": "john.doe@x.dummyjson.com"
  },
  {
   "rating": 2,
   "comment": "Not as described!",
   "date": "2024-05-23T08:56:21.618Z",
   "reviewerName": "Nolan Gonzalez",
   "reviewerEmail": "nolan.gonzalez@x.dummyjson.com"
  },
  {
   "rating": 5,
   "comment": "Very satisfied!",
   "date": "2024-05-23T08:56:21.618Z",
   "reviewerName": "Scarlett Wright",
   "reviewerEmail": "scarlett.wright@x.dummyjson.com"
  }
 ],
 "returnPolicy": "30 days return policy",
 "minimumOrderQuantity": 24,
 "meta": {
  "createdAt": "2024-05-23T08:56:21.618Z",
  "updatedAt": "2024-05-23T08:56:21.618Z",
  "barcode": "9164035109868",
  "qrCode": "..."
 },
 "thumbnail": "...",
 "images": ["...", "...", "..."],
}

URL:https://xxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev?property=title
‘property’というキーに’title’を指定して、titleのみをレスポンスとして返すようにします。
クエリ文字列を変更することで動的にレスポンスを変更することができます。

統合レスポンスのマッピングテンプレートを以下の内容に変更します。
application/json

#set($allParams = $input.params())
{
 #foreach($type in $allParams.keySet())
  #set($params = $allParams.get($type))
  #foreach($paramName in $params.keySet())
   #if($paramName == "property")
    #set($queryString = $util.escapeJavaScript($params.get($paramName)))
    #break
   #end
  #end
 #end
 #if($queryString)
  #set($inputRoot = $input.json('$'))
  #set($property = '$.$queryString')
  #foreach($keyName in $util.parseJson($inputRoot).keySet())
   #if($keyName == $queryString)
    "property": "$util.parseJson($inputRoot).get($keyName)"
   #end
  #end
 #end
}

レスポンス結果

{
 "property": "Essence Mascara Lash Princess"
}

ポイントとして、15行目と17行目で$inputRootを$util.parseJsonを使用してKeyValue型に変換している箇所があります。
これにより、keySet()やget()を使用して、それぞれのKeyとValueを取得することができます。

具体的には、以下のような流れになります:
1.JSONデータのパース:
$inputRootに対して$util.parseJsonを使用することで、JSON形式のデータをKeyValue型に変換します。
例: $inputRoot = $util.parseJson($input.body)

2.KeyとValueの取得:
変換後のデータに対して、keySet()メソッドを使用してすべてのキーを取得できます。
例: $inputRoot.keySet()

また、get()メソッドを使用して特定のキーに対応する値を取得できます。
例: $inputRoot.get(‘keyName’)

Apache VTLを使用する際にはデータがKeyValue型なのかJSON型なのかを意識することが重要です。
これにより、適切なメソッドや関数を使用してデータを操作することができます。

まとめ

API GatewayのマッピングテンプレートでVTLをうまく使えば
Lambda関数なしでAPI Gatewayで受け取ったデータの変換と検証ができ
Lambda関数の運用・保守工数と課金を0にできます。
一方でLambda関数よりも可読性が落ちたり、VTLのテストや管理をどうするのかなど
新たに考えるべきことも発生します。

API Gatewayのマッピングテンプレートで実現できることで
今後、ロジックに変更がないのであればマッピングテンプレートに移行するのも有効かと思いました。