はじめに
注文チームは、JSON の注文を受け付け、計算した合計を返す必要があります。このラボでは、ヘルスチェック、書き込み、読み取りのルートを、提供された Lambda アプリケーションに接続します。有効なリクエストと拒否されるリクエストをテストし、削除する前に、保存された DynamoDB のレコードを確認します。
最初に HTTP API で Lambda 関数を公開する と Lambda から DynamoDB を読み書きする を完了してください。この新しい環境には、アプリケーション、ロール、独立したテーブルが用意されています。
認定試験との関連
| 認定資格 | 試験タスク | 演習内容 |
|---|---|---|
| Solutions Architect – Associate (SAA-C03) | タスク 2.1 | API リクエスト検証、レスポンス、保存された業務データ。 |
| Developer – Associate (DVA-C02) | タスク 1.1, タスク 3.2 | API リクエスト検証、レスポンス、保存された業務データ。 |
| DevOps Engineer – Professional (DOP-C02) | タスク 3.2 | 基礎演習:API リクエスト検証、レスポンス、保存された業務データ。 |
ラボの概要

注文のバックエンドを準備する
このステップでは、提供されたリクエストの仕様を確認し、関数に注文テーブルへのアクセスを許可して、バックエンドをデプロイします。アプリケーションは、整数のセント単位で、数量 × 250 + 100 セントの手数料を計算します。上部の AWS View を使い、リクエストと保存データを比較してください。
作業ディレクトリに移動し、アプリケーションを読みます。
cd /home/labex/project
cat app.py
handler は HTTP ペイロード形式 2.0 を受け取ります。POST /orders は、正確に 2 つの JSON フィールドを要求します。id は小文字の 2~40 文字で、英字で始まり、英字、数字、ハイフンを使います。quantity は 1~10 の整数です。コードは PutItem の前に、それ以外の形式を拒否します。GET /orders?id=... は、GetItem で保存されたレコードを読み取ります。各レスポンスには、HTTP ステータスと JSON 本文が含まれます。提供されたアプリケーションを書き直す必要はありません。
準備済みのテーブル一覧を読み取ります。
aws dynamodb list-tables --query TableNames
labex-a02-orders と labex-a02-reference が表示されるはずです。この API の業務処理に属するのは、注文テーブルだけです。その ARN をシェル変数に読み込みます。
TABLE_ARN=$(aws dynamodb describe-table \
--table-name labex-a02-orders \
--query Table.TableArn \
--output text)
echo "$TABLE_ARN"
実行ロールは、関数からデータベースへのアクセスを制御します。ヒアドキュメントでポリシーファイルを作成します。引用符のない JSON マーカーにより、$TABLE_ARN は読み取った ARN に展開されます。認証情報は含まれていません。
cat > orders-policy.json <<JSON
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["dynamodb:PutItem", "dynamodb:GetItem"],
"Resource": "$TABLE_ARN"
}]
}
JSON
この名前付きのインラインポリシーを、準備済みの実行ロールに付けます。
aws iam put-role-policy \
--role-name labex-a02-execution \
--policy-name OrdersData \
--policy-document file://orders-policy.json
アーカイブの最上位に app.py を置いて、提供されたコードをパッケージ化します。
zip orders.zip app.py
準備済みのロール ARN を読み取ります。
ROLE_ARN=$(aws iam get-role \
--role-name labex-a02-execution \
--query Role.Arn \
--output text)
echo "$ROLE_ARN"
10 秒のタイムアウトで関数をデプロイします。アプリケーションは、環境から TABLE_NAME を読み取り、テーブルの選択をソースコードから分離します。
aws lambda create-function \
--function-name labex-a02-orders-api \
--runtime python3.12 \
--handler app.handler \
--role "$ROLE_ARN" \
--timeout 10 \
--environment '{"Variables":{"TABLE_NAME":"labex-a02-orders"}}' \
--zip-file fileb://orders.zip \
--query '{Name:FunctionName,Handler:Handler,Table:Environment.Variables.TABLE_NAME}'
関数名、app.handler、labex-a02-orders が返るはずです。AWS View で Function カードを確認し、注文テーブルのアイテム数がまだゼロであることを確認します。コードのデプロイとデータアクセスの許可では、注文は作成されません。
ヘルスルートと注文ルートを対応付ける
このステップでは、3 つのルートに 1 つの統合を設定し、各ルートに関数の呼び出しを許可します。
HTTP API を作成し、その ID を保存します。
API_ID=$(aws apigatewayv2 create-api \
--name labex-a02 \
--protocol-type HTTP \
--query ApiId \
--output text)
echo "$API_ID"
printf '%s\n' "$API_ID" > api-id.txt
文字どおりの $default ステージを、自動デプロイで作成します。
aws apigatewayv2 create-stage \
--api-id "$API_ID" \
--stage-name '$default' \
--auto-deploy \
--query '{Stage:StageName,AutoDeploy:AutoDeploy}'
バックエンド ARN を読み取り、ペイロード 2.0 の統合を作成します。その POST メソッドは Lambda を呼び出すためのものです。各クライアントのルートには、引き続きそれぞれの HTTP メソッドがあります。
FUNCTION_ARN=$(aws lambda get-function-configuration \
--function-name labex-a02-orders-api \
--query FunctionArn \
--output text)
echo "$FUNCTION_ARN"
INTEGRATION_ID=$(aws apigatewayv2 create-integration \
--api-id "$API_ID" \
--integration-type AWS_PROXY \
--integration-method POST \
--integration-uri "$FUNCTION_ARN" \
--payload-format-version 2.0 \
--query IntegrationId \
--output text)
echo "$INTEGRATION_ID"
公開のヘルスエンドポイントを、この統合に対応付けます。
aws apigatewayv2 create-route \
--api-id "$API_ID" \
--route-key 'GET /health' \
--target "integrations/$INTEGRATION_ID" \
--authorization-type NONE \
--query RouteKey \
--output text
JSON の書き込みルートを対応付けます。
aws apigatewayv2 create-route \
--api-id "$API_ID" \
--route-key 'POST /orders' \
--target "integrations/$INTEGRATION_ID" \
--authorization-type NONE \
--query RouteKey \
--output text
保存された注文の読み取りルートを対応付けます。
aws apigatewayv2 create-route \
--api-id "$API_ID" \
--route-key 'GET /orders' \
--target "integrations/$INTEGRATION_ID" \
--authorization-type NONE \
--query RouteKey \
--output text
実行ロールのデータベースポリシーは、API Gateway に関数の呼び出しを許可しません。アカウント ID を読み取り、3 つの正確な呼び出し許可を追加します。各ステートメントは、API ID、文字どおりの $default、クライアントのメソッド、ルートパスを使います。\$ は、展開するシェル文字列内でドル記号を保持します。
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
echo "$ACCOUNT_ID"
aws lambda add-permission \
--function-name labex-a02-orders-api \
--statement-id ApiHealth \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-account "$ACCOUNT_ID" \
--source-arn "arn:aws:execute-api:us-east-1:$ACCOUNT_ID:$API_ID/\$default/GET/health" \
--query Statement \
--output text
aws lambda add-permission \
--function-name labex-a02-orders-api \
--statement-id ApiWriteOrder \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-account "$ACCOUNT_ID" \
--source-arn "arn:aws:execute-api:us-east-1:$ACCOUNT_ID:$API_ID/\$default/POST/orders" \
--query Statement \
--output text
aws lambda add-permission \
--function-name labex-a02-orders-api \
--statement-id ApiReadOrder \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-account "$ACCOUNT_ID" \
--source-arn "arn:aws:execute-api:us-east-1:$ACCOUNT_ID:$API_ID/\$default/GET/orders" \
--query Statement \
--output text
生成された ID を使い、準備済みの作業環境の API アドレスを作成します。このアドレスは作業環境内でリクエストを処理するもので、AWS の公開ホスト名ではありません。
API_URL="http://127.0.0.1:8081/api/$API_ID"
echo "$API_URL"
ヘルスルートをテストします。curl -i は、HTTP レスポンスのヘッダーを含めます。
curl -i "$API_URL/health"
HTTP 200 と {"healthy": true} が返るはずです。AWS View で、3 つのルートキーがすべて NONE、ステージが AutoDeploy 有効で、ヘルスチェックの呼び出しが 200 を返したことを確認します。このリクエストは注文の保存ではなくヘルスチェックなので、注文テーブルはまだ空のはずです。

有効な注文と拒否されるリクエストをテストする
このステップでは、2 件の注文を作成し、保存された結果を読み取り、失敗したリクエストが業務データに影響しないことを確認します。
HTTP リクエストは、メソッド、パス、ヘッダー、任意の本文で構成されます。-X POST は書き込みメソッドを選び、-H は Content-Type: application/json を渡し、--data は JSON 文字列を送ります。数量 2 の注文を作成します。
curl -i -X POST "$API_URL/orders" -H 'Content-Type: application/json' --data '{"id":"order-one","quantity":2}'
HTTP 201 Created と、order-one、数量 2、total_cents: 600(2 × 250 + 100)を含む本文が返るはずです。ステータスは、注文が保存されたことをクライアントに伝え、本文は計算結果を報告します。
異なる数量で、2 件目の注文を作成します。
curl -i -X POST "$API_URL/orders" -H 'Content-Type: application/json' --data '{"id":"order-two","quantity":5}'
HTTP 201 と total_cents: 1350 が返るはずです。クエリパラメータを使い、API を通じて両方の注文を読み取ります。
curl -i "$API_URL/orders?id=order-one"
curl -i "$API_URL/orders?id=order-two"
両方のレスポンスは HTTP 200 で、対応する作成レスポンスと同じ ID、数量、合計になるはずです。読み取りハンドラーは、新しいクライアントの数量から合計を再計算するのではなく、DynamoDB の状態を取得します。
CLI で、最初の保存されたアイテムを独立して確認します。
aws dynamodb get-item \
--table-name labex-a02-orders \
--key '{"id":{"S":"order-one"}}' \
--consistent-read \
--query Item
型付きアイテムには、quantity: {"N":"2"} と total_cents: {"N":"600"} が含まれます。AWS View で、実際の 2 件のテーブルレコードと、POST および GET の呼び出しレスポンスを比較します。
構文上は有効な JSON リクエストでも、業務の仕様に違反することがあります。負の数量を送ります。
curl -i -X POST "$API_URL/orders" -H 'Content-Type: application/json' --data '{"id":"bad-negative","quantity":-1}'
HTTP 400 Bad Request と Use an id and an integer quantity from 1 to 10 が返るはずです。JSON 文字列で表した数値も拒否されます。
curl -i -X POST "$API_URL/orders" -H 'Content-Type: application/json' --data '{"id":"bad-type","quantity":"2"}'
最後に、形式の壊れた JSON 本文をテストします。
curl -i -X POST "$API_URL/orders" -H 'Content-Type: application/json' --data '{not-json'
最後の 2 つのリクエストも、HTTP 400 を返します。実際のハンドラーイベントが CloudWatch Logs に表示されることはありますが、テーブルには有効な 2 件の注文だけが残っていなければなりません。リクエストの検証は、提供されたアプリケーション内で、PutItem の前に行われます。API ルートが一致しただけでは、本文は検証されません。
読み取り ID が有効でも、保存された注文がないことがあります。まだ作成していない注文をクエリします。
curl -i "$API_URL/orders?id=missing-order"
HTTP 404 と Order not found が返るはずです。これは無効なリクエストの 400 とは異なります。ID は期待された形式ですが、アイテムが存在しません。上部の AWS View を確認し、実際の 201、200、400、404 のレスポンスを確認します。呼び出しの Show logs を展開し、入力と結果を比較してください。この手動の UI 確認は、リモートのレコードと実際の実行に対する、独立した読み取り専用のチェックを補完します。

DynamoDB tables カードまでスクロールし、自分の環境のフィールドを比較してください。
これらの 400 レスポンスはアプリケーションの検証を試します。リクエストは Lambda に到達し、関数が書き込み前に入力を拒否します。後で学ぶ JWT の拒否は、Lambda に到達する前に API Gateway でリクエストを止めます。実行ログと保存された項目を比較して、どの境界で拒否されたかを確認してください。
API と所有する注文リソースを削除する
このステップでは、無関係な参照テーブル、ログ、準備済みの実行ロールを保持しながら、API のリソースを削除します。
リソース一覧は、api-id.txt の API ID、labex-a02-orders-api、その /aws/lambda/labex-a02-orders-api ロググループ、labex-a02-orders、ロールの OrdersData インラインポリシーです。最初に、ルート、統合、default ステージを含めて API を削除します。
aws apigatewayv2 delete-api --api-id "$API_ID"
関数とその呼び出しポリシーを削除します。
aws lambda delete-function --function-name labex-a02-orders-api
別のリソースである関数ロググループを削除します。
aws logs delete-log-group --log-group-name /aws/lambda/labex-a02-orders-api
所有する注文テーブルと、そのデータを削除します。
aws dynamodb delete-table \
--table-name labex-a02-orders \
--query TableDescription.TableName \
--output text
準備済みのロールに追加したデータポリシーだけを削除します。
aws iam delete-role-policy --role-name labex-a02-execution --policy-name OrdersData
削除を証明するため、正常に一覧を読み取ります。認証やネットワークのエラーは、削除の証拠にはなりません。
aws apigatewayv2 get-apis --query 'Items[].Name'
aws lambda list-functions --query 'Functions[].FunctionName'
aws dynamodb list-tables --query TableNames
最初の 2 つのリストは空で、テーブル一覧には labex-a02-reference だけが含まれるはずです。元のアイテムが残っていることを確認します。
aws dynamodb get-item \
--table-name labex-a02-reference \
--key '{"id":{"S":"reference"}}' \
--query Item
message: {"S":"keep unchanged"} が返るはずです。ロールには、無関係なログ出力ポリシーが残っているはずです。
aws iam list-role-policies --role-name labex-a02-execution --query PolicyNames
FunctionLogs だけが表示されるはずです。AWS View では、API、Function、呼び出し記録がなくなり、参照テーブルと INFO platform reference keep unchanged が残っているはずです。これらの準備済みリソースは保持してください。テスト用認証情報と VM の終了は、リソース確認の後に扱われます。
まとめ
正確なデータベースポリシーと API の呼び出し許可を使い、提供された Lambda アプリケーションに 3 つの HTTP ルートを接続しました。異なる計算結果の注文を作成して取得し、実際の DynamoDB レコードを確認して、成功、無効、注文が存在しない場合のレスポンスを区別しました。その後、参照リソースを保持しながら、API とその所有するデータ、関数、ログ、ポリシーを削除しました。
詳しくは、AWS 公式ドキュメントを参照してください:HTTP API の Lambda イベントとレスポンス形式; HTTP API のルートとクエリパラメータ。



