AWS の開始方法
サーバーレスのウェブアプリケーションを構築する
AWS Lambda、Amazon API Gateway、AWS Amplify、Amazon DynamoDB、および Amazon Cognito を使用
モジュール 3: サーバーレスサービスバックエンド
AWS Lambda と Amazon DynamoDB を使用して、ウェブアプリケーションのリクエストを処理するためのバックエンドプロセスを構築します
概要
このモジュールでは、AWS Lambda と Amazon DynamoDB を使用して、ウェブアプリケーションのリクエストを処理するためのバックエンドプロセスを構築します。最初のモジュールにデプロイしたブラウザアプリケーションを使用すると、ユーザーはユニコーンを自分の好きな場所に送信するように要求できます。これらのリクエストを満たすには、ブラウザで実行されている JavaScript がクラウドで実行されているサービスを呼び出す必要があります。
アーキテクチャの概要
ユーザーがユニコーンを要求するたびに呼び出される Lambda 関数を実装します。この関数は、フリートからユニコーンを選択し、その要求を DynamoDB テーブルに記録してから、ディスパッチされているユニコーンに関する詳細を使用して、フロントエンドアプリケーションに応答します。
この関数は、Amazon API Gateway を使用してブラウザから呼び出されます。次のモジュールでその接続を実装します。このモジュールで必要なのは、ご利用の関数を単独でテストすることだけです。
所要時間
30 分
利用するサービス
実装
-
Amazon DynamoDB テーブルを作成する
Amazon DynamoDB コンソールを使用して、新しい DynamoDB テーブルを作成します。
- Amazon DynamoDB コンソールで、[テーブルの作成] を選択します。
- [テーブル名] に「Rides」と入力します。 このフィールドでは、大文字と小文字が区別されます。
- [パーティションキー] に「RideId」と入力し、キータイプとして [文字列] を選択します。このフィールドでは、大文字と小文字が区別されます。
- [テーブル設定] セクションで、[デフォルト設定] が選択されていることを確認し、[テーブルの作成] を選択します。
- [テーブル] ページで、テーブルの作成が完了するまで待ちます。完了すると、ステータスは [アクティブ] になります。テーブル名を選択します。
- 新しいテーブルの [概要] タブ > [一般情報] セクションで、[追加情報] を選択します。ARN をコピーします。これは次のセクションで使用します。
-
Lambda 関数の IAM ロールを作成する
すべての Lambda 関数には、それに関連付けられた IAM ロールがあります。このロールは、その関数が他のどの AWS のサービスとやり取りできるかを定義します。このチュートリアルの目的のために、ログを Amazon CloudWatch Logs に書き込むアクセス許可と、項目を DynamoDB テーブルに書き込むアクセス許可を Lambda 関数に付与する IAM ロールを作成する必要があります。
- IAM コンソールの左側のナビゲーションペインで [ロール] を選択し、[ロールの作成] を選択します。
- [信頼されたエンティティタイプ] セクションで、[AWS サービス] を選択します。[ユースケース] で [Lambda] を選択し、[次へ] を選択します。
注: ロールタイプを選択すると、AWS のサービスがお客様に代わってこのロールを引き受けることを許可する、お客様のロールの信頼ポリシーが自動的に作成されます。CLI、AWS CloudFormation、またはその他のメカニズムを使用してこのロールを作成している場合は、信頼ポリシーを直接指定します。 - フィルターテキストボックスに「AWSLambdaBasicExecutionRole」と入力し、Enter キーを押します。
- AWSLambdaBasicExecutionRole ポリシー名の横にあるチェックボックスをオンにして [次へ] を選択します。
- [ロール名] に「WildRydesLambda」と入力します。他のパラメータはデフォルト設定のままにします。
- [ロールの作成] を選択します。
- [ロール] ページのフィルターボックスに「WildRydesLambda」と入力し、先ほど作成したロールの名前を選択します。
- [アクセス許可] タブの [アクセス許可の追加] で [インラインポリシーを作成] を選択します。
- [サービスの選択] セクションで、検索バーに「DynamoDB」と入力し、表示されたら [DynamoDB] を選択します。
- [アクションの選択] を選択します。
- [許可されているアクション] セクションで、検索バーに「PutItem」と入力し、表示されたら [PutItem] の横にあるチェックボックスをオンにします。
- [リソース] セクションで、[特定] オプションが選択されている状態で [ARN を追加] リンクを選択します。
- [テキスト] タブを選択します。DynamoDB で作成したテーブルの ARN を貼り付け (前のセクションのステップ 6)、[ARN を追加] を選択します。
- [次へ] をクリックします。
- ポリシー名として「DynamoDBWriteAccess」と入力し、[ポリシーの作成] を選択します。
-
リクエスト処理のために Lambda 関数を作成する
AWS Lambda は、HTTP リクエストなどのイベントに応答してコードを実行します。このステップでは、ウェブアプリケーションからの API リクエストを処理してユニコーンをディスパッチするコア機能を構築します。次のモジュールでは、Amazon API Gateway を使用して、ユーザーのブラウザから呼び出すことができる HTTP エンドポイントを公開する RESTful API を作成します。その後、このステップで作成した Lambda 関数をその API に接続して、ウェブアプリケーション用の完全に機能的なバックエンドを作成します。
AWS Lambda コンソールを使用して、API リクエストを処理する RequestUnicorn という名前の新しい Lambda 関数を作成します。次の requestUnicorn.js 実装例を関数コードに使用してください。必要なのは、そのファイルから AWS Lambda コンソールのエディタにコピーして貼り付けることだけです。
前のセクションで作成した WildRydesLambda IAM ロールを使用するように関数が設定されていることを確認します。
- AWS Lambda コンソールで、[関数の作成] をクリックします。
- デフォルトの [一から作成] カードを選択したままにします。
- [関数名] フィールドに「RequestUnicorn」と入力します。
- [ランタイム] には [Node.js 16.x] を選択します (新しいバージョンの Node.js はこのチュートリアルでは動作しません)。
- [デフォルトの実行ロールの変更] ドロップダウンから [既存のロールを使用する] を選択します。
- [既存のロール] ドロップダウンで [WildRydesLambda] を選択します。
- [関数を作成] をクリックします。
- [コードソース] セクションまでスクロールし、index.js コードエディタの既存のコードを requestUnicorn.js の内容に置き換えます。次のコードブロックは requestUnicorn.js ファイルを表示します。このコードをコピーして、コードエディタの [index.js] タブに貼り付けます。
const randomBytes = require('crypto').randomBytes; const AWS = require('aws-sdk'); const ddb = new AWS.DynamoDB.DocumentClient(); const fleet = [ { Name: 'Angel', Color: 'White', Gender: 'Female', }, { Name: 'Gil', Color: 'White', Gender: 'Male', }, { Name: 'Rocinante', Color: 'Yellow', Gender: 'Female', }, ]; exports.handler = (event, context, callback) => { if (!event.requestContext.authorizer) { errorResponse('Authorization not configured', context.awsRequestId, callback); return; } const rideId = toUrlString(randomBytes(16)); console.log('Received event (', rideId, '): ', event); // Because we're using a Cognito User Pools authorizer, all of the claims // included in the authentication token are provided in the request context. // This includes the username as well as other attributes. const username = event.requestContext.authorizer.claims['cognito:username']; // The body field of the event in a proxy integration is a raw string. // In order to extract meaningful values, we need to first parse this string // into an object. A more robust implementation might inspect the Content-Type // header first and use a different parsing strategy based on that value. const requestBody = JSON.parse(event.body); const pickupLocation = requestBody.PickupLocation; const unicorn = findUnicorn(pickupLocation); recordRide(rideId, username, unicorn).then(() => { // You can use the callback function to provide a return value from your Node.js // Lambda functions. The first parameter is used for failed invocations. The // second parameter specifies the result data of the invocation. // Because this Lambda function is called by an API Gateway proxy integration // the result object must use the following structure. callback(null, { statusCode: 201, body: JSON.stringify({ RideId: rideId, Unicorn: unicorn, Eta: '30 seconds', Rider: username, }), headers: { 'Access-Control-Allow-Origin': '*', }, }); }).catch((err) => { console.error(err); // If there is an error during processing, catch it and return // from the Lambda function successfully. Specify a 500 HTTP status // code and provide an error message in the body. This will provide a // more meaningful error response to the end client. errorResponse(err.message, context.awsRequestId, callback) }); }; // This is where you would implement logic to find the optimal unicorn for // this ride (possibly invoking another Lambda function as a microservice.) // For simplicity, we'll just pick a unicorn at random. function findUnicorn(pickupLocation) { console.log('Finding unicorn for ', pickupLocation.Latitude, ', ', pickupLocation.Longitude); return fleet[Math.floor(Math.random() * fleet.length)]; } function recordRide(rideId, username, unicorn) { return ddb.put({ TableName: 'Rides', Item: { RideId: rideId, User: username, Unicorn: unicorn, RequestTime: new Date().toISOString(), }, }).promise(); } function toUrlString(buffer) { return buffer.toString('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=/g, ''); } function errorResponse(errorMessage, awsRequestId, callback) { callback(null, { statusCode: 500, body: JSON.stringify({ Error: errorMessage, Reference: awsRequestId, }), headers: { 'Access-Control-Allow-Origin': '*', }, }); }
9.[デプロイ] を選択します。
-
実装を検証する
このモジュールでは、AWS Lambda コンソールを使用して構築した関数をテストします。次のモジュールでは、最初のモジュールでデプロイしたブラウザベースのアプリケーションから関数を呼び出せるようにするために、API Gateway を使用して REST API を追加します。
- 前のセクションで作成した RequestUnicorn 関数の [コードソース] セクションで [テスト] を選択し、ドロップダウンから [テストイベントの設定] を選択します。
- [新しいイベントを作成] はデフォルトのままにします。
- [イベント名] フィールドに「TestRequestEvent」と入力します。
- 次のテストイベントをコピーして、[イベント JSON] セクションに貼り付けます。
{ "path": "/ride", "httpMethod": "POST", "headers": { "Accept": "*/*", "Authorization": "eyJraWQiOiJLTzRVMWZs", "content-type": "application/json; charset=UTF-8" }, "queryStringParameters": null, "pathParameters": null, "requestContext": { "authorizer": { "claims": { "cognito:username": "the_username" } } }, "body": "{\"PickupLocation\":{\"Latitude\":47.6174755835663,\"Longitude\":-122.28837066650185}}" }
5.[Save] を選択します。
6.関数の [コードソース] セクションで [テスト] を選択し、ドロップダウンから [TestRequestEvent] を選択します。
7. [テスト] タブの [テスト] を選択します。
8.表示される「関数の実行: 成功」メッセージで、[詳細] ドロップダウンを展開します。
9.関数の結果が次のようになっていることを確認します。
{ "statusCode": 201, "body": "{\"RideId\":\"SvLnijIAtg6inAFUBRT+Fg==\",\"Unicorn\":{\"Name\":\"Rocinante\",\"Color\":\"Yellow\",\"Gender\":\"Female\"},\"Eta\":\"30 seconds\"}", "headers": { "Access-Control-Allow-Origin": "*" } }