

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

# AWS AppSync リゾルバーマッピングテンプレートプログラミングガイド
<a name="resolver-mapping-template-reference-programming-guide"></a>

**注記**  
現在、主に APPSYNC\$1JS ランタイムとそのドキュメントをサポートしています。[こちら](https://docs.aws.amazon.com/appsync/latest/devguide/resolver-reference-js-version.html) で APPSYNC\$1JS ランタイムとそのガイドの使用をご検討ください。

これは、Apache Velocity Template Language (VTL) を使用したプログラミングのクックブック形式のチュートリアルです AWS AppSync。JavaScript、C、Java など他のプログラミング言語に慣れている場合は、かなり容易に進められます。

AWS AppSync は VTL を使用して、クライアントからの GraphQL リクエストをデータソースへのリクエストに変換します。その後、このプロセスを逆転して、データソースレスポンスを GraphQL レスポンスに変換します。VTL はロジカルなテンプレート言語で、ウェブアプリケーションの標準的なリクエスト/レスポンスフローで、次のような手法を利用した、リクエストとレスポンスの両方を操作する機能が用意されています。
+ 新しい項目のデフォルト値
+ 入力の検証とフォーマット
+ データ変換および整形
+ リスト、マップ、配列の値を取り出す、または変更するための繰り返し処理
+ ユーザー ID に基づくレスポンスのフィルタリング/変更
+ 複合認可チェック

たとえば、GraphQL 引数でサービスの電話番号の検証を実行する、または DynamoDB に格納する前に入力パラメータを大文字に変換する場合を考えます。または、クライアントシステムで、GraphQL 引数、JWT トークンクレーム、または HTTP ヘッダーの一部としてコードを渡し、コードがリストの特定の文字列と一致する場合にのみ、データを返す場合を考えます。これらはすべて、 AWS AppSync の VTL で実行できる論理チェックです。

VTL では、使い慣れているプログラミング手法を使用してロジックを適用できます。ただし、標準的なリクエスト/レスポンスフロー内での実行に限られています。これにより、ユーザーベースが拡大しても、GraphQL API のスケーラビリティが確保されます。 AWS AppSync はリゾルバーとして もサポート AWS Lambda しているため、柔軟性が必要な場合は、選択したプログラミング言語 (Node.js、Python、Go、Java など) で Lambda 関数を記述できます。

## セットアップ
<a name="setup"></a>

プログラミング言語を学ぶ一般的な手法は、結果 (たとえば、JavaScript の `console.log(variable)`) を出力し、何が起こるのかを確認することです。このチュートリアルでは、シンプルな GraphQL スキーマを作成し、Lambda 関数に値のマップを渡すことで、このデモを実行します。Lambda 関数で値を出力し、それらに応答します。これにより、リクエスト/レスポンスフローを理解し、さまざまなプログラミング手法を確認できます。

以下の GraphQL スキーマを作成することから開始します。

```
type Query {
    get(id: ID, meta: String): Thing
}

type Thing {
    id: ID!
    title: String!
    meta: String
}

schema {
    query: Query
}
```

ここで、Node.js を言語として使用して、次の AWS Lambda 関数を作成します。

```
exports.handler = (event, context, callback) => {
    console.log('VTL details: ', event);
    callback(null, event);
};
```

 AWS AppSync コンソールの**データソース**ペインで、この Lambda 関数を新しいデータソースとして追加します。コンソールの [**スキーマ**] ページに戻り、右側で、`get(...):Thing` クエリの横にある [**アタッチ**] ボタンをクリックします。リクエストテンプレートでは、[**Invoke and forward arguments (引数の呼び出しと転送)**] メニューから既存のテンプレートを選択します。レスポンステンプレートでは、[**Return Lambda result (Lambda 関数の結果を返す)**] を選択します。

Lambda 関数の Amazon CloudWatch Logs を 1 か所で開き、 AWS AppSync コンソールの**クエリ**タブから次の GraphQL クエリを実行します。

```
query test {
  get(id:123 meta:"testing"){
    id
    meta
  }
}
```

GraphQL のレスポンスには、`id:123` と `meta:testing` が含まれます。Lambda 関数がエコーバックするためです。数秒後、CloudWatch Logs でこれらの詳細とともにレコードが表示されます。

## [変数]
<a name="variables"></a>

VTL では[参照](https://velocity.apache.org/engine/1.7/user-guide.html#references)を使用します。これらの参照を使用してデータを保存または操作できます。VTL には 3 種類の参照型 (変数、プロパティ、およびメソッド) があります。変数には、先頭に `$` 記号が付き、`#set` ディレクティブで作成されます。

```
#set($var = "a string")
```

変数は、他の言語で使い慣れた、同様の型 (数値、文字列、配列、リスト、マップなど) に保存します。Lambda リゾルバーのデフォルトリクエストテンプレートで JSON ペイロードが送信されていることがわかります。

```
"payload": $util.toJson($context.arguments)
```

ここで注意すべき点がいくつかあります。まず、 AWS AppSync は一般的なオペレーションのためのいくつかの便利な関数を提供します。この例では、`$util.toJson` は変数を JSON に変換します。次に、変数 `$context.arguments` は GraphQL リクエストからマップオブジェクトとして自動的に入力されます。新しいマップを次のように作成できます。

```
#set( $myMap = {
  "id": $context.arguments.id,
  "meta": "stuff",
  "upperMeta" : $context.arguments.meta.toUpperCase()
} )
```

`$myMap` という変数が作成されました。これには `id`、`meta`、および `upperMeta` のキーがあります。これは、以下の特徴も示しています。
+  `id` には、GraphQL 引数からキーが入力されます。これは VTL で一般的で、クライアントから引数を取得するためのものです。
+  `meta` は、値がハードコードされ、デフォルト値を示します。
+  `upperMeta` は `meta` 引数を `.toUpperCase()` メソッドを使用して変換します。

リクエストテンプレートの先頭に以前のコードを配置して、`payload` を変更して、新しい `$myMap` 変数を使用するようにします。

```
"payload": $util.toJson($myMap)
```

Lambda 関数を実行し、CloudWatch のログのデータとともに、レスポンスの変更を確認できます。このチュートリアルの残りの部分を実行するので、同様のテストを実行できるように `$myMap` の入力を保持します。

変数に *properties\$1* を設定することもできます。これらは単純な文字列、配列、または JSON です。

```
#set($myMap.myProperty = "ABC")
#set($myMap.arrProperty = ["Write", "Some", "GraphQL"])
#set($myMap.jsonProperty = {
    "AppSync" : "Offline and Realtime",
    "Cognito" : "AuthN and AuthZ"
})
```

### 静的参照
<a name="quiet-references"></a>

VTL がテンプレート作成言語であるため、デフォルトでは、そこで使用するすべての参照は `.toString()` を実行します。参照が未定義の場合、実際の参照表現を、文字列として出力します。例:

```
#set($myValue = 5)
##Prints '5'
$myValue

##Prints '$somethingelse'
$somethingelse
```

これに対応するために、VTL には「*静的参照*」、または「*サイレント参照*」構文があり、この動作を抑制するかどうかをテンプレートエンジンに指示します。この構文は `$!{}` です。たとえば、前のコードで `$!{somethingelse}` を使用してわずかに変更した場合、印刷は抑制されます。

```
#set($myValue = 5)
##Prints '5'
$myValue

##Nothing prints out
$!{somethingelse}
```

## メソッドの呼び出し
<a name="calling-methods"></a>

前の例では、変数を作成し、同時に値を設定する方法を説明しました。次に示すように、データをマップに追加して、これを 2 つのステップで実行することもできます。

```
#set ($myMap = {})
#set ($myList = [])

##Nothing prints out
$!{myMap.put("id", "first value")}
##Prints "first value"
$!{myMap.put("id", "another value")}
##Prints true
$!{myList.add("something")}
```

 **ただし**、この動作について留意すべき点があります。静的参照表記 `$!{}` で、上記のようにメソッドを呼び出すことができますが、実行されたメソッドの戻り値は抑制されません。そのため `##Prints "first value"` と `##Prints true` が追加されています。キーがすでに存在している場合に、値を挿入するなど、リストやマップで反復処理をしているときに、エラーが発生する場合があります。出力で評価時に予期しない文字列がテンプレートに追加されるためです。

これに対する回避策は、`#set` ディレクティブを使用してメソッドを呼び出し、変数を無視するとうまくいくことがあります。例えば、次のようになります。

```
#set ($myMap = {})
#set($discard = $myMap.put("id", "first value"))
```

テンプレートでこの手法を使用すると、予期しない文字列がテンプレートに出力されるのを防ぐことができます。 AWS AppSync は、より簡潔な表記で同じ動作を提供する代替の便利な関数を提供します。これにより、これらの実装の詳細を検討する必要がなくなります。`$util.quiet()` またはそのエイリアス `$util.qr()` でこの関数にアクセスできます。例えば、次のようになります。

```
#set ($myMap = {})
#set ($myList = [])

##Nothing prints out
$util.quiet($myMap.put("id", "first value"))
##Nothing prints out
$util.qr($myList.add("something"))
```

## 文字列
<a name="strings"></a>

多くのプログラミング言語の場合と同じように、文字列は、特に変数から構築する場合に対処が困難なことがあります。VTL で発生する共通の事項がいくつかあります。

DynamoDB のようなデータソースに文字列としてデータを挿入する場合、GraphQL 引数などの変数から入力されます。文字列は、二重引用符で囲まれ、文字列で変数を参照するために必要なのは `"${}"` だけです ([静的参照表記](https://velocity.apache.org/engine/1.7/user-guide.html#quiet-reference-notation)の `!` がない場合)。これは JavaScript のテンプレートリテラル ([https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template\$1literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals)) に似ています。

```
#set($firstname = "Jeff")
$!{myMap.put("Firstname", "${firstname}")}
```

GraphQL クライアントから引数を使用するときの `"author": { "S" : "${context.arguments.author}"}`、または `"id" : { "S" : "$util.autoId()"}` のような ID 自動生成などの DynamoDB リクエストテンプレートでこれを確認できます。つまり、データを入力するため、文字列の中でメソッドの結果または変数を参照できるということです。

部分文字列を取り出すなど、Java [String クラス](https://docs.oracle.com/javase/6/docs/api/java/lang/String.html)のパブリックメソッドを使用することもできます。

```
#set($bigstring = "This is a long string, I want to pull out everything after the comma")
#set ($comma = $bigstring.indexOf(','))
#set ($comma = $comma +2)
#set ($substring = $bigstring.substring($comma))

$util.qr($myMap.put("substring", "${substring}"))
```

文字列連結も非常に一般的なタスクです。これを行うには、変数参照を単独で、または静的な値とともに使用します。

```
#set($s1 = "Hello")
#set($s2 = " World")

$util.qr($myMap.put("concat","$s1$s2"))
$util.qr($myMap.put("concat2","Second $s1 World"))
```

## loop
<a name="loops"></a>

変数を作成して、メソッドを呼び出したので、ロジックをコードに追加できます。他の言語とは異なり、VTL では反復回数が事前に定義されている場合にのみ、ループが許可されます。Velocity には `do..while` はありません。この設計により、評価プロセスは常に確実に終了します。これは GraphQL オペレーションを実行するときに、スケーラビリティのための境界になります。

ループは `#foreach` で作成され、**ループ変数**や、配列、リスト、マップ、コレクションなど**反復可能オブジェクト**の入力が必要です。`#foreach` ループの典型的なプログラミング例では、コレクションの項目をループし、出力します。このケースでは、それらを取り出し、マップに追加します。

```
#set($start = 0)
#set($end = 5)
#set($range = [$start..$end])

#foreach($i in $range)
   ##$util.qr($myMap.put($i, "abc"))
   ##$util.qr($myMap.put($i, $i.toString()+"foo")) ##Concat variable with string
   $util.qr($myMap.put($i, "${i}foo"))     ##Reference a variable in a string with "${varname}"
#end
```

この例では、いくつかの注意点を示します。まず、範囲 `[..]` 演算子で変数を使用して、反復可能オブジェクトを作成します。次に、各項目は、操作できる `$i` 変数によって参照されます。前の例では、**コメント**は、二重の `##` 記号で示されます。これは、文字列を使用した、異なるメソッドの連結であるとともに、キー (値) の両方に、ループ変数を使用する例でもあります。

`$i` は整数であることに注意してください。`.toString()` メソッドを呼び出すことができます。GraphQL の INT タイプでは、これが役に立ちます。

範囲演算子を直接使用することもできます。次に例を示します。

```
#foreach($item in [1..5])
    ...
#end
```

## 配列
<a name="arrays"></a>

ここまで、マップを操作しましたが、配列も VTL で一般的です。配列では、`.isEmpty()`、`.size()`、`.set()`、`.get()`、`.add()` などのいくつかの基本メソッドで、以下のようにアクセスできます。

```
#set($array = [])
#set($idx = 0)

##adding elements
$util.qr($array.add("element in array"))
$util.qr($myMap.put("array", $array[$idx]))

##initialize array vals on create
#set($arr2 = [42, "a string", 21, "test"])

$util.qr($myMap.put("arr2", $arr2[$idx]))
$util.qr($myMap.put("isEmpty", $array.isEmpty()))  ##isEmpty == false
$util.qr($myMap.put("size", $array.size()))

##Get and set items in an array
$util.qr($myMap.put("set", $array.set(0, 'changing array value')))
$util.qr($myMap.put("get", $array.get(0)))
```

前の例では配列インデックス表記を使用して `arr2[$idx]` の要素を取得しました。同様に、マップ/ディクショナリから名前で参照することができます。

```
#set($result = {
    "Author" : "Nadia",
    "Topic" : "GraphQL"
})

$util.qr($myMap.put("Author", $result["Author"]))
```

条件式を使用して、レスポンステンプレートでデータソースから返される結果をフィルタリングするときに、これがよく使われます。

## 条件チェック
<a name="conditional-checks"></a>

前の `#foreach` のセクションで、VTL でデータを変換するロジックを使用するいくつかの例を示しました。データを評価する条件チェックがランタイムにも適用されます。

```
#if(!$array.isEmpty())
    $util.qr($myMap.put("ifCheck", "Array not empty"))
#else
    $util.qr($myMap.put("ifCheck", "Your array is empty"))
#end
```

上記のブール式の `#if()` チェックは、問題ありませんが、分岐に演算子と `#elseif()` を使用することもできます。

```
#if ($arr2.size() == 0)
    $util.qr($myMap.put("elseIfCheck", "You forgot to put anything into this array!"))
#elseif ($arr2.size() == 1)
    $util.qr($myMap.put("elseIfCheck", "Good start but please add more stuff"))
#else
    $util.qr($myMap.put("elseIfCheck", "Good job!"))
#end
```

これらの 2 つの例は否定 (\$1) と一致 (==) を示しています。\$1\$1、&&、>、<、>=、<=、および \$1= を使用することもできます。

```
#set($T = true)
#set($F = false)

#if ($T || $F)
  $util.qr($myMap.put("OR", "TRUE"))
#end

#if ($T && $F)
  $util.qr($myMap.put("AND", "TRUE"))
#end
```

 **注意 :** `Boolean.FALSE` と `null` だけが条件で false と見なされます。ゼロ (0) と空の文字列 ("") は false に該当しません。

## オペレータ
<a name="operators"></a>

何らかの演算処理を実行する演算子がないと、プログラミング言語は完全とはいえません。手始めにいくつか例を紹介します。

```
#set($x = 5)
#set($y = 7)
#set($z = $x + $y)
#set($x-y = $x - $y)
#set($xy = $x * $y)
#set($xDIVy = $x / $y)
#set($xMODy = $x % $y)

$util.qr($myMap.put("z", $z))
$util.qr($myMap.put("x-y", $x-y))
$util.qr($myMap.put("x*y", $xy))
$util.qr($myMap.put("x/y", $xDIVy))
$util.qr($myMap.put("x|y", $xMODy))
```

### ループと条件式を組み合わせて使用する
<a name="loops-and-conditionals-together"></a>

オブジェクトをループしアクションを実行する前にチェックを実行することは、データソースからの書き込みや読み込みの前など、VTL でデータを変換する際によく使われます。前のセクションのいくつかのツールを組み合わせることで、多くの機能が利用できます。1 つの便利な手法は `#foreach` では各項目に `.count` が自動的に使用できる点を理解することです。

```
#foreach ($item in $arr2)
  #set($idx = "item" + $foreach.count)
  $util.qr($myMap.put($idx, $item))
#end
```

たとえば、一定サイズ未満の値をマップから取り出す場合があります。`#break` ステートメントで、条件とともにカウントを使用してこれを実行できます。

```
#set($hashmap = {
  "DynamoDB" : "https://aws.amazon.com/dynamodb/",
  "Amplify" : "https://github.com/aws/aws-amplify",
  "DynamoDB2" : "https://aws.amazon.com/dynamodb/",
  "Amplify2" : "https://github.com/aws/aws-amplify"
})

#foreach ($key in $hashmap.keySet())
    #if($foreach.count > 2)
    #break
  #end
    $util.qr($myMap.put($key, $hashmap.get($key)))
#end
```

前の `#foreach` はマップに対して使用できる `.keySet()` で反復します。これにより、`$key` を取得し `.get($key)` で値を参照するためにアクセスできます。 AWS AppSync のクライアントからの GraphQL 引数はマップとして保存されます。また、`.entrySet()` で反復処理を実行でき、Set としてのキーと値の両方にアクセスでき、入力の検証や変換などの複雑な条件チェックを実行するか、他の変数を入力できます。

```
#foreach( $entry in $context.arguments.entrySet() )
#if ($entry.key == "XYZ" && $entry.value == "BAD")
    #set($myvar = "...")
  #else
    #break
  #end
#end
```

他の一般的な例は、データを同期するときの初期オブジェクトバージョンなど、デフォルト情報の自動入力です (競合の解決に非常に重要)。または認可チェック用のオブジェクトのデフォルト所有者もあります。Mary がこのブログ記事を作成した場合は以下のようになります。

```
#set($myMap.owner ="Mary")
#set($myMap.defaultOwners = ["Admins", "Editors"])
```

## Context
<a name="context"></a>

 AWS AppSync リゾルバーと VTL での論理チェック実行に慣れてきたところで、コンテキストオブジェクトを確認します。

```
$util.qr($myMap.put("context", $context))
```

これには、GraphQL リクエストの、アクセスできるすべての情報が含まれます。詳細な説明については、[コンテキストリファレンス](resolver-context-reference.md#aws-appsync-resolver-mapping-template-context-reference)を参照してください。

## フィルタリング
<a name="filtering"></a>

これまでチュートリアルで、Lambda 関数からのすべての情報が、非常にシンプルな JSON 変換と GraphQL クエリに返されます。

```
$util.toJson($context.result)
```

VTL ロジックは、データソースからレスポンスを取得するとき、特にリソースで許可チェックを実行するときに強力です。いくつかの例を見てみましょう。最初に、次のようにレスポンステンプレートの変更を試します。

```
#set($data = {
    "id" : "456",
    "meta" : "Valid Response"
})

$util.toJson($data)
```

GraphQL オペレーションで何が起こるかにかかわらず、ハードコードされた値がクライアントに返されます。`meta` フィールドに Lambda レスポンスから値が設定されるように少し変更し、条件式についての前のチュートリアルのように、`elseIfCheck` 値に設定します。

```
#set($data = {
    "id" : "456"
})

#foreach($item in $context.result.entrySet())
    #if($item.key == "elseIfCheck")
        $util.qr($data.put("meta", $item.value))
    #end
#end

$util.toJson($data)
```

 `$context.result` はマップで、キーまたは値のいずれかで返されるロジックを実行するために `entrySet()` を使用できます。GraphQL オペレーションを実行したユーザーに関する情報が `$context.identity` に含まれているため、データソースから認可情報を返す場合は、ロジックに基づいて、ユーザーに返すデータ (すべて、一部、なし) を決定できます。次のようにレスポンステンプレートを変更します。

```
#if($context.result["id"] == 123)
    $util.toJson($context.result)
  #else
    $util.unauthorized()
#end
```

GraphQL クエリを実行した場合、データがノーマルとして返されます。ただし、id 引数を 123 以外のものに変更した場合 (`query test { get(id:456 meta:"badrequest"){} }`)、認可失敗のメッセージが表示されます。

[認可ユースケース](security-authorization-use-cases.md#aws-appsync-security-authorization-use-cases)セクションに他の認可シナリオがあります。

### テンプレートサンプル
<a name="appendix-template-sample"></a>

このチュートリアルを実行していくと、ステップごとに拡張されて、このテンプレートになります。このテンプレートがまだない場合は、以下のテンプレートをテスト用にコピーします。

 **リクエストテンプレート** 

```
#set( $myMap = {
  "id": $context.arguments.id,
  "meta": "stuff",
  "upperMeta" : "$context.arguments.meta.toUpperCase()"
} )

##This is how you would do it in two steps with a "quiet reference" and you can use it for invoking methods, such as .put() to add items to a Map
#set ($myMap2 = {})
$util.qr($myMap2.put("id", "first value"))

## Properties are created with a dot notation
#set($myMap.myProperty = "ABC")
#set($myMap.arrProperty = ["Write", "Some", "GraphQL"])
#set($myMap.jsonProperty = {
    "AppSync" : "Offline and Realtime",
    "Cognito" : "AuthN and AuthZ"
})

##When you are inside a string and just have ${} without ! it means stuff inside curly braces are a reference
#set($firstname = "Jeff")
$util.qr($myMap.put("Firstname", "${firstname}"))

#set($bigstring = "This is a long string, I want to pull out everything after the comma")
#set ($comma = $bigstring.indexOf(','))
#set ($comma = $comma +2)
#set ($substring = $bigstring.substring($comma))
$util.qr($myMap.put("substring", "${substring}"))

##Classic for-each loop over N items:
#set($start = 0)
#set($end = 5)
#set($range = [$start..$end])
#foreach($i in $range)          ##Can also use range operator directly like #foreach($item in [1...5])
   ##$util.qr($myMap.put($i, "abc"))
   ##$util.qr($myMap.put($i, $i.toString()+"foo")) ##Concat variable with string
   $util.qr($myMap.put($i, "${i}foo"))     ##Reference a variable in a string with "${varname)"
#end

##Operators don't work
#set($x = 5)
#set($y = 7)
#set($z = $x + $y)
#set($x-y = $x - $y)
#set($xy = $x * $y)
#set($xDIVy = $x / $y)
#set($xMODy = $x % $y)
$util.qr($myMap.put("z", $z))
$util.qr($myMap.put("x-y", $x-y))
$util.qr($myMap.put("x*y", $xy))
$util.qr($myMap.put("x/y", $xDIVy))
$util.qr($myMap.put("x|y", $xMODy))

##arrays
#set($array = ["first"])
#set($idx = 0)
$util.qr($myMap.put("array", $array[$idx]))
##initialize array vals on create
#set($arr2 = [42, "a string", 21, "test"])
$util.qr($myMap.put("arr2", $arr2[$idx]))
$util.qr($myMap.put("isEmpty", $array.isEmpty()))  ##Returns false
$util.qr($myMap.put("size", $array.size()))
##Get and set items in an array
$util.qr($myMap.put("set", $array.set(0, 'changing array value')))
$util.qr($myMap.put("get", $array.get(0)))

##Lookup by name from a Map/dictionary in a similar way:
#set($result = {
    "Author" : "Nadia",
    "Topic" : "GraphQL"
})
$util.qr($myMap.put("Author", $result["Author"]))


##Conditional examples
#if(!$array.isEmpty())
$util.qr($myMap.put("ifCheck", "Array not empty"))
#else
$util.qr($myMap.put("ifCheck", "Your array is empty"))
#end

#if ($arr2.size() == 0)
$util.qr($myMap.put("elseIfCheck", "You forgot to put anything into this array!"))
#elseif ($arr2.size() == 1)
$util.qr($myMap.put("elseIfCheck", "Good start but please add more stuff"))
#else
$util.qr($myMap.put("elseIfCheck", "Good job!"))
#end

##Above showed negation(!) and equality (==), we can also use OR, AND, >, <, >=, <=, and !=
#set($T = true)
#set($F = false)
#if ($T || $F)
  $util.qr($myMap.put("OR", "TRUE"))
#end

#if ($T && $F)
  $util.qr($myMap.put("AND", "TRUE"))
#end

##Using the foreach loop counter - $foreach.count
#foreach ($item in $arr2)
  #set($idx = "item" + $foreach.count)
  $util.qr($myMap.put($idx, $item))
#end

##Using a Map and plucking out keys/vals
#set($hashmap = {
    "DynamoDB" : "https://aws.amazon.com/dynamodb/",
    "Amplify" : "https://github.com/aws/aws-amplify",
    "DynamoDB2" : "https://aws.amazon.com/dynamodb/",
    "Amplify2" : "https://github.com/aws/aws-amplify"
})

#foreach ($key in $hashmap.keySet())
    #if($foreach.count > 2)
        #break
    #end
    $util.qr($myMap.put($key, $hashmap.get($key)))
#end

##concatenate strings
#set($s1 = "Hello")
#set($s2 = " World")
$util.qr($myMap.put("concat","$s1$s2"))
$util.qr($myMap.put("concat2","Second $s1 World"))

$util.qr($myMap.put("context", $context))

{
    "version" : "2017-02-28",
    "operation": "Invoke",
    "payload": $util.toJson($myMap)
}
```

 **レスポンステンプレート** 

```
#set($data = {
"id" : "456"
})
#foreach($item in $context.result.entrySet())   ##$context.result is a MAP so we use entrySet()
    #if($item.key == "ifCheck")
        $util.qr($data.put("meta", "$item.value"))
    #end
#end

##Uncomment this out if you want to test and remove the below #if check
##$util.toJson($data)

#if($context.result["id"] == 123)
    $util.toJson($context.result)
  #else
    $util.unauthorized()
#end
```