チュートリアル: DynamoDB トランザクションリゾルバー - AWS AppSync

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

チュートリアル: DynamoDB トランザクションリゾルバー

注記

現在、主に APPSYNC_JS ランタイムとそのドキュメントをサポートしています。こちらにある APPSYNC_JS ランタイムとそのガイドの使用をご検討ください。

AWS AppSync は、単一リージョン内での複数のテーブルを対象とした Amazon DynamoDB トランザクションオペレーションの使用をサポートします。サポートされている処理は、TransactGetItems および TransactWriteItems です。AWS AppSync でこれらの機能を使用すると、次のようなタスクを実行できます。

  • 単一クエリでキーのリストを渡し、テーブルからの結果を返す

  • 単一クエリで 1 つ以上のテーブルからレコードを読み取る

  • トランザクション内のレコードを 1 つまたは複数のテーブルにオールオアナッシング方式で書き込みます

  • いくつかの条件が満たされたときにトランザクションを実行します

許可

他のリゾルバーと同様に、AWS AppSync にデータソースを作成し、ロールを作成または既存のロールを使用する必要があります。トランザクションオペレーションでは DynamoDB テーブルごとに異なるアクセス許可が必要であるため、読み取りまたは書き込みアクションのために設定されたアクセス許可がロールに必要です。

{ "Version": "2012-10-17", "Statement": [ { "Action": [ "dynamodb:DeleteItem", "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:Query", "dynamodb:Scan", "dynamodb:UpdateItem" ], "Effect": "Allow", "Resource": [ "arn:aws:dynamodb:region:accountId:table/TABLENAME", "arn:aws:dynamodb:region:accountId:table/TABLENAME/*" ] } ] }

注意 : ロールは AWS AppSync のデータソースに関連付けられ、フィールドのリゾルバーはデータソースに対して呼び出されます。DynamoDB に対して取得するよう設定されたデータソースは、設定を単純にするために 1 つのテーブルのみ指定されます。したがって、単一のリゾルバーで複数のテーブルに対してトランザクションオペレーションを実行する場合、これはより高度なタスクであり、リゾルバーが利用するすべてのテーブルへのアクセスをデータソースのロールに許可する必要があります。これは、上記の IAM ポリシーの Resource フィールドで行われます。テーブルに対するトランザクション呼び出しの設定は、リゾルバーのテンプレートで行います。これについては以下で説明します。

データソース

分かりやすくするために、このチュートリアルではすべてのリゾルバーに同じデータソースを使用します。[Data sources (データソース)] タブで、新しい DynamoDB データソースを作成し、TransactTutorial という名前を付けます。テーブル名はトランザクションオペレーションのリクエストマッピングテンプレートで指定するので、何でも構いません。ここでは、テーブル名に empty を使用しています。

savingAccounts および checkingAccounts というテーブルが 2 つできます。両方ともパーティションキーとして accountNumbertransactionHistory テーブルtransactionIdと一緒に持ちます。

このチュートリアルでは、次のインラインポリシーを含むロールが使用できます。region および accountId をお客様のリージョンとアカウント ID に置き換えます。

{ "Version": "2012-10-17", "Statement": [ { "Action": [ "dynamodb:DeleteItem", "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:Query", "dynamodb:Scan", "dynamodb:UpdateItem" ], "Effect": "Allow", "Resource": [ "arn:aws:dynamodb:region:accountId:table/savingAccounts", "arn:aws:dynamodb:region:accountId:table/savingAccounts/*", "arn:aws:dynamodb:region:accountId:table/checkingAccounts", "arn:aws:dynamodb:region:accountId:table/checkingAccounts/*", "arn:aws:dynamodb:region:accountId:table/transactionHistory", "arn:aws:dynamodb:region:accountId:table/transactionHistory/*" ] } ] }

トランザクション

この例では、コンテキストは従来の銀行取引で、次の目的で TransactWriteItems を使用します。

  • 普通預金から当座預金への振替

  • 取引ごとの新しい取引レコードの生成

次に、TransactGetItems を使用して、普通預金と当座預金の詳細を取得します。

次のように GraphQL スキーマを定義します。

type SavingAccount { accountNumber: String! username: String balance: Float } type CheckingAccount { accountNumber: String! username: String balance: Float } type TransactionHistory { transactionId: ID! from: String to: String amount: Float } type TransactionResult { savingAccounts: [SavingAccount] checkingAccounts: [CheckingAccount] transactionHistory: [TransactionHistory] } input SavingAccountInput { accountNumber: String! username: String balance: Float } input CheckingAccountInput { accountNumber: String! username: String balance: Float } input TransactionInput { savingAccountNumber: String! checkingAccountNumber: String! amount: Float! } type Query { getAccounts(savingAccountNumbers: [String], checkingAccountNumbers: [String]): TransactionResult } type Mutation { populateAccounts(savingAccounts: [SavingAccountInput], checkingAccounts: [CheckingAccountInput]): TransactionResult transferMoney(transactions: [TransactionInput]): TransactionResult } schema { query: Query mutation: Mutation }

TransactWriteItems - 口座の入力

口座間で振替を行うには、テーブルに詳細を入力する必要があります。これを実行するには、GraphQL オペレーション Mutation.populateAccounts を使用します。

[スキーマ] セクションで、Mutation.populateAccounts オペレーションの横にあるアタッチをクリックします。VTL ユニットリゾルバーに移動し、同じ TransactTutorial データソースを選択してください。

ここで、次のリクエストマッピングテンプレートを使用します。

リクエストマッピングテンプレート

#set($savingAccountTransactPutItems = []) #set($index = 0) #foreach($savingAccount in ${ctx.args.savingAccounts}) #set($keyMap = {}) $util.qr($keyMap.put("accountNumber", $util.dynamodb.toString($savingAccount.accountNumber))) #set($attributeValues = {}) $util.qr($attributeValues.put("username", $util.dynamodb.toString($savingAccount.username))) $util.qr($attributeValues.put("balance", $util.dynamodb.toNumber($savingAccount.balance))) #set($index = $index + 1) #set($savingAccountTransactPutItem = {"table": "savingAccounts", "operation": "PutItem", "key": $keyMap, "attributeValues": $attributeValues}) $util.qr($savingAccountTransactPutItems.add($savingAccountTransactPutItem)) #end #set($checkingAccountTransactPutItems = []) #set($index = 0) #foreach($checkingAccount in ${ctx.args.checkingAccounts}) #set($keyMap = {}) $util.qr($keyMap.put("accountNumber", $util.dynamodb.toString($checkingAccount.accountNumber))) #set($attributeValues = {}) $util.qr($attributeValues.put("username", $util.dynamodb.toString($checkingAccount.username))) $util.qr($attributeValues.put("balance", $util.dynamodb.toNumber($checkingAccount.balance))) #set($index = $index + 1) #set($checkingAccountTransactPutItem = {"table": "checkingAccounts", "operation": "PutItem", "key": $keyMap, "attributeValues": $attributeValues}) $util.qr($checkingAccountTransactPutItems.add($checkingAccountTransactPutItem)) #end #set($transactItems = []) $util.qr($transactItems.addAll($savingAccountTransactPutItems)) $util.qr($transactItems.addAll($checkingAccountTransactPutItems)) { "version" : "2018-05-29", "operation" : "TransactWriteItems", "transactItems" : $util.toJson($transactItems) }

さらに、以下のレスポンスマッピングテンプレートがあるとします。

レスポンスマッピングテンプレート

#if ($ctx.error) $util.appendError($ctx.error.message, $ctx.error.type, null, $ctx.result.cancellationReasons) #end #set($savingAccounts = []) #foreach($index in [0..2]) $util.qr($savingAccounts.add(${ctx.result.keys[$index]})) #end #set($checkingAccounts = []) #foreach($index in [3..5]) $util.qr($checkingAccounts.add(${ctx.result.keys[$index]})) #end #set($transactionResult = {}) $util.qr($transactionResult.put('savingAccounts', $savingAccounts)) $util.qr($transactionResult.put('checkingAccounts', $checkingAccounts)) $util.toJson($transactionResult)

リゾルバーを保存し、AWS AppSync コンソールのクエリセクションを使用してアカウントを設定します。

次のミューテーションを実行します。

mutation populateAccounts { populateAccounts ( savingAccounts: [ {accountNumber: "1", username: "Tom", balance: 100}, {accountNumber: "2", username: "Amy", balance: 90}, {accountNumber: "3", username: "Lily", balance: 80}, ] checkingAccounts: [ {accountNumber: "1", username: "Tom", balance: 70}, {accountNumber: "2", username: "Amy", balance: 60}, {accountNumber: "3", username: "Lily", balance: 50}, ]) { savingAccounts { accountNumber } checkingAccounts { accountNumber } } }

1 つのミューテーションで 3 つの普通預金と 3 つの当座預金に入力しました。

DynamoDB コンソールを使用して、savingAccounts テーブルと checkingAccounts テーブルの両方にデータが表示されることを確認します。

TransactWriteItems - 振替

次のリクエストマッピングテンプレートを使用して transferMoney ミューテーションにリゾルバーをアタッチします。amountssavingAccountNumbers、および checkingAccountNumbers の値は同じであることに注意してください。

#set($amounts = []) #foreach($transaction in ${ctx.args.transactions}) #set($attributeValueMap = {}) $util.qr($attributeValueMap.put(":amount", $util.dynamodb.toNumber($transaction.amount))) $util.qr($amounts.add($attributeValueMap)) #end #set($savingAccountTransactUpdateItems = []) #set($index = 0) #foreach($transaction in ${ctx.args.transactions}) #set($keyMap = {}) $util.qr($keyMap.put("accountNumber", $util.dynamodb.toString($transaction.savingAccountNumber))) #set($update = {}) $util.qr($update.put("expression", "SET balance = balance - :amount")) $util.qr($update.put("expressionValues", $amounts[$index])) #set($index = $index + 1) #set($savingAccountTransactUpdateItem = {"table": "savingAccounts", "operation": "UpdateItem", "key": $keyMap, "update": $update}) $util.qr($savingAccountTransactUpdateItems.add($savingAccountTransactUpdateItem)) #end #set($checkingAccountTransactUpdateItems = []) #set($index = 0) #foreach($transaction in ${ctx.args.transactions}) #set($keyMap = {}) $util.qr($keyMap.put("accountNumber", $util.dynamodb.toString($transaction.checkingAccountNumber))) #set($update = {}) $util.qr($update.put("expression", "SET balance = balance + :amount")) $util.qr($update.put("expressionValues", $amounts[$index])) #set($index = $index + 1) #set($checkingAccountTransactUpdateItem = {"table": "checkingAccounts", "operation": "UpdateItem", "key": $keyMap, "update": $update}) $util.qr($checkingAccountTransactUpdateItems.add($checkingAccountTransactUpdateItem)) #end #set($transactionHistoryTransactPutItems = []) #foreach($transaction in ${ctx.args.transactions}) #set($keyMap = {}) $util.qr($keyMap.put("transactionId", $util.dynamodb.toString(${utils.autoId()}))) #set($attributeValues = {}) $util.qr($attributeValues.put("from", $util.dynamodb.toString($transaction.savingAccountNumber))) $util.qr($attributeValues.put("to", $util.dynamodb.toString($transaction.checkingAccountNumber))) $util.qr($attributeValues.put("amount", $util.dynamodb.toNumber($transaction.amount))) #set($transactionHistoryTransactPutItem = {"table": "transactionHistory", "operation": "PutItem", "key": $keyMap, "attributeValues": $attributeValues}) $util.qr($transactionHistoryTransactPutItems.add($transactionHistoryTransactPutItem)) #end #set($transactItems = []) $util.qr($transactItems.addAll($savingAccountTransactUpdateItems)) $util.qr($transactItems.addAll($checkingAccountTransactUpdateItems)) $util.qr($transactItems.addAll($transactionHistoryTransactPutItems)) { "version" : "2018-05-29", "operation" : "TransactWriteItems", "transactItems" : $util.toJson($transactItems) }

単一の TransactWriteItems オペレーションで 3 つの銀行取引を行います。以下のレスポンスマッピングテンプレートを使用します。

#if ($ctx.error) $util.appendError($ctx.error.message, $ctx.error.type, null, $ctx.result.cancellationReasons) #end #set($savingAccounts = []) #foreach($index in [0..2]) $util.qr($savingAccounts.add(${ctx.result.keys[$index]})) #end #set($checkingAccounts = []) #foreach($index in [3..5]) $util.qr($checkingAccounts.add(${ctx.result.keys[$index]})) #end #set($transactionHistory = []) #foreach($index in [6..8]) $util.qr($transactionHistory.add(${ctx.result.keys[$index]})) #end #set($transactionResult = {}) $util.qr($transactionResult.put('savingAccounts', $savingAccounts)) $util.qr($transactionResult.put('checkingAccounts', $checkingAccounts)) $util.qr($transactionResult.put('transactionHistory', $transactionHistory)) $util.toJson($transactionResult)

次に AWS AppSync コンソールのクエリセクションに移動して、以下のように transferMoney ミューテーションを実行します。

mutation write { transferMoney( transactions: [ {savingAccountNumber: "1", checkingAccountNumber: "1", amount: 7.5}, {savingAccountNumber: "2", checkingAccountNumber: "2", amount: 6.0}, {savingAccountNumber: "3", checkingAccountNumber: "3", amount: 3.3} ]) { savingAccounts { accountNumber } checkingAccounts { accountNumber } transactionHistory { transactionId } } }

1 つのミューテーションで 2 つの銀行取引を送信しました。DynamoDB コンソールを使用して、savingAccountscheckingAccounts、および transactionHistory の各テーブルにデータが表示されることを検証します。

TransactGetItems - 口座の取得

1 つの取引リクエストで普通預金口座と当座預金口座から詳細を取得するために、スキーマで Query.getAccounts GraphQL オペレーションにリゾルバーをアタッチします。[アタッチ] を選択し、次の画面で、このチュートリアルの最初で作成したのと同じ TransactTutorial データソースを選択します。テンプレートを次のように設定します。

リクエストマッピングテンプレート

#set($savingAccountsTransactGets = []) #foreach($savingAccountNumber in ${ctx.args.savingAccountNumbers}) #set($savingAccountKey = {}) $util.qr($savingAccountKey.put("accountNumber", $util.dynamodb.toString($savingAccountNumber))) #set($savingAccountTransactGet = {"table": "savingAccounts", "key": $savingAccountKey}) $util.qr($savingAccountsTransactGets.add($savingAccountTransactGet)) #end #set($checkingAccountsTransactGets = []) #foreach($checkingAccountNumber in ${ctx.args.checkingAccountNumbers}) #set($checkingAccountKey = {}) $util.qr($checkingAccountKey.put("accountNumber", $util.dynamodb.toString($checkingAccountNumber))) #set($checkingAccountTransactGet = {"table": "checkingAccounts", "key": $checkingAccountKey}) $util.qr($checkingAccountsTransactGets.add($checkingAccountTransactGet)) #end #set($transactItems = []) $util.qr($transactItems.addAll($savingAccountsTransactGets)) $util.qr($transactItems.addAll($checkingAccountsTransactGets)) { "version" : "2018-05-29", "operation" : "TransactGetItems", "transactItems" : $util.toJson($transactItems) }

レスポンスマッピングテンプレート

#if ($ctx.error) $util.appendError($ctx.error.message, $ctx.error.type, null, $ctx.result.cancellationReasons) #end #set($savingAccounts = []) #foreach($index in [0..2]) $util.qr($savingAccounts.add(${ctx.result.items[$index]})) #end #set($checkingAccounts = []) #foreach($index in [3..4]) $util.qr($checkingAccounts.add($ctx.result.items[$index])) #end #set($transactionResult = {}) $util.qr($transactionResult.put('savingAccounts', $savingAccounts)) $util.qr($transactionResult.put('checkingAccounts', $checkingAccounts)) $util.toJson($transactionResult)

リゾルバーを保存し、AppSyncコンソールの[クエリ]AWSセクションに移動します。普通預金口座と当座預金口座を取得するには、次のクエリを実行します。

query getAccounts { getAccounts( savingAccountNumbers: ["1", "2", "3"], checkingAccountNumbers: ["1", "2"] ) { savingAccounts { accountNumber username balance } checkingAccounts { accountNumber username balance } } }

以上で、AWS AppSync を使用した DynamoDB のトランザクションのデモンストレーションが完了しました。