はじめに
皆さん、こんにちは。AWSソリューションアーキテクトの福井です。本連載は、AWS Lambda Powertools Pythonの提供する様々なユーティリティーについて、その利用方法を紹介するシリーズですが、この度、Lambda Powertools for TypeScript も一般利用可能 (GA) となりました。普段、Lambda 関数を TypeScript で記述されている方々にとっては嬉しいお知らせではないでしょうか。この連載を参考にしつつ、TypeScript 版もぜひお試しください。
さて、第 4 回 では、AWS Lambda Powertools Python のMetricsユーティリティーの使い方についてご紹介しました。今回は Core Utilities の中の Event Handler についてご紹介します。なお、本シリーズでは、AWS Lambda Powertools Python 1.25.1 を対象としています。
builders.flash メールメンバー登録
Event Handler (Core Utilities)
REST API
Amazon API Gateway の REST API、HTTP API、および Application Load Balancer (ALB) のためのイベントハンドラを提供します。REST API イベントハンドラの主要な機能は以下の通りです。
- API Gateway REST / HTTP API と ALB に対するボイラープレートを減らす軽量なルーティング
- CORS、バイナリーと Gzip 圧縮、Decimal の JSON エンコーディング、独自の JSON シリアライザをサポート
- 自己記述型イベントスキーマのための Event Source Data Classes ユーティリティーとの統合
利用方法
AWS Lambda Powertools Python を Lambda 関数に組み込む方法については、第 1 回 の記事を参照ください。既存の Amazon API Gateway でプロキシ統合を設定するか、ALB で Lambda 関数を呼び出すように設定しておく必要があります。
AWS Serverless Application Model (SAM) の例です。
AWS Serverless Application Model (SAM) の例
コマンド / コード
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
restapi-eventhandler-demo
Globals:
Api:
TracingEnabled: true
Cors:
AllowOrigin: "'*'"
AllowHeaders: "'Content-Type,Authorization,X-Amz-Date'"
MaxAge: "'300'"
BinaryMediaTypes:
- "*~1*"
EndpointConfiguration:
Type: REGIONAL
Function:
Timeout: 5
Runtime: python3.9
Tracing: Active
Environment:
Variables:
LOG_LEVEL: INFO
POWERTOOLS_LOGGER_SAMPLE_RATE: 0.1
POWERTOOLS_LOGGER_LOG_EVENT: true
POWERTOOLS_SERVICE_NAME: restapi-eventhandler-demo
Resources:
RestAPiFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ./src
Handler: app.lambda_handler
Runtime: python3.9
Architectures:
- x86_64
Layers:
- !Sub arn:aws:lambda:${AWS::Region}:017000801446:layer:AWSLambdaPowertoolsPython:11
Events:
AnyApiEvent:
Type: Api
Properties:
Path: /{proxy+}
Method: ANY
イベントリゾルバ
関数のデコレーターをセットしてリクエストの path や HTTP のメソッドをハンドルする前に、リゾルバを初期化する必要があります。リゾルバはリクエストを解決するために、1 つ以上のルーティング処理を含みます。また型付けされたプロパティーを通じて現在のイベントにアクセスできるようにします。
リゾルバとして、APIGatewayRestResolver、APIGatewayHttpResolver、ALBResolver を提供しています。
Amazon API Gateway REST API
Lambda 関数のフロントとして、Amazon API Gateway の REST API を利用する場合は、APIGatewayRestResolver を利用することができます。
例えば、Amazon API Gateway の REST API から GET リクエストの path で /products をハンドルする方法は以下の通りです。

レスポンスの例
API Gateway の公開している URL に /products のパスをつけて呼び出すと Lambda 関数は API Gateway に対して以下のレスポンスを返します。
{ "statusCode": 200, "headers": { "Content-Type": "application/json" }, "body": "{\"products\":[{\"id\":\"0001\",\"name\":\"M01\",\"price\":1000}, {\"id\":\"0002\",\"name\":\"M02\",\"price\":2000}, {\"id\":\"0003\",\"name\":\"M03\",\"price\":1500}]}", "isBase64Encoded": false }
クライアントへのレスポンス
このレスポンスを受け取った Amazon API Gateway は、Response の StatusCode として 200 を返し、以下の JSON メッセージをクライアント側に返します。 このように、ルートに対するメソッドを定義し、辞書型のオブジェクトをリターンするだけのシンプルな実装で、クライアント側にその値を JSON として返すことができます。
{
"products": [
{
"id": "0001",
"name": "M01",
"price": 1000
},
{
"id": "0002",
"name": "M02",
"price": 2000
},
{
"id": "0003",
"name": "M03",
"price": 1500
}
]
}
Amazon API Gateway HTTP API
Amazon API Gateway の HTTP API を利用する場合は以下のようなコードになります。違いは、APIGatewayRestResolver の代わりに APIGatewayHttpResolver を利用するところです。

Application Load Balancer
同様に、ALB のロードバランサをイベントソースとして Lambda 関数を実行する場合は、APIGatewayRestResolver の代わりに、ALBResolver を利用します。
動的ルーティング
動的な URL パスを構成するために、/products/<product_id> の形式を利用することができます。<product_id> は、実行時に解決されます。
個々の動的ルーティングは関数のシグネチャの一部である必要があります。これによって動的ルーティングにマッチした時に、キーワード引数を利用して関数が呼び出されます。

0001 での実行結果
この Lambda 関数をデプロイして、API Gateway の URL に /products/0001 のパスをつけて呼び出した結果は以下の通りです。
{"id":"0001","name":"M01","price":1000}
0004 での実行結果
同様に /products/0004 のパスで実行すると以下のような結果がクライアント側に返ります。動的パスはネストすることも可能です。例えば、/products/<product_id>/<product_status> のような呼び出しもできます。
{"message":"no match!"}
すべてのルートを補足する

HTTP メソッド
Lambda 関数でハンドルする HTTP メソッドを指定するために名前付きデコレーター (app.get など) を利用することができます。指定可能なメソッドは、get, post, put, patch, delete, option です。
例えば、post メソッドをハンドルするには、このようなコードを記述します。

複数の HTTP メソッド
もし 1 つの関数で複数の HTTP メソッドを受けたい場合は、route メソッドを利用して HTTP メソッドのリストを送ります。
一般的には、HTTP メソッドごとに個別の関数を定義するほうが望ましいです。なぜなら関数で実装する機能は、どの HTTP メソッドを利用するかに依存するからです。

リクエストの詳細へのアクセス
Event Handler は、Event Source Data Classes Utility と統合されており、このユーティリティーは個々のリゾルバのリクエストの詳細と app.current_event の便利なメソッドを提供しています。
すべてのサンプルソースに app.resolve(event, context) のコードが記述されています。これによって Event Handler がリクエストを解決することができ、app.lambda_context や app.current_event のようなデータを取得することができます。
クエリ文字列とペイロード
app.current_event プロパティ内の query_string_parameters を通じて、すべての利用可能なクエリ文字列にアクセスすることができます。または特定のクエリ文字列を get_query_string_value メソッドで取得することができます。
body プロパティを利用すると生のペイロードにアクセスすることができます。また、Body の 値が JSON 文字列の場合は、 上記の HTTP メソッドの app.post のサンプルコードのように、json_body プロパティを利用して素早くデシリアイズすることができます。

headers の取得

実行結果
実行結果は以下の通りです。
{"trace_id":"Root=1-62e8614b-5dc882a310b97e464ab07ee4"}
not found ルートのハンドリング

レスポンスの例
このコードをデプロイして存在しないパスを指定して呼び出すと API Gateway に対して以下のレスポンスを返します。
{ "statusCode": 404, "headers": { "Content-Type": "application/json" }, "body": "{\"StatusCode\": 404, \"error_message\": \"\u898b\u3064\u304b\u308a\u307e\u305b\u3093\"}", "isBase64Encoded": false }
クライアントへのレスポンスの例
このレスポンスを受けて API Gateway はクライアントに StatusCode 400 と共に以下の結果が返されます。
{"StatusCode": 404, "error_message": "見つかりません"}
例外のハンドリング

実行結果の例
この Lambda 関数に対して、/products/1 のパスを指定した場合クライアントに {"quantity":1} のレスポンスが返りますが、数字以外の値をパラメータとしてパスに指定すると ValueError が発生します。例えば、/products/a を指定すると StatusCode 400 と共に以下のメッセージがクライアント側に返されます。
Invalid request parameters.
HTTP エラーを発生させる
ServiceError 例外を使用することで、あらゆる HTTP エラーを発生させクライアントに戻すことができます。これによって Lambda 関数が失敗することなく、エラーを通知するための正しい HTTP レスポンスをクライアントに返すことができます。
Tips:もしカスタムヘッダを送信したい場合は、代わりに Response クラスを利用します。
HTTP 400、401、404、500 など、良く利用されるエラーは事前定義エラーとして提供されています。

実行結果の例
この Lambda 関数に対して、例えば /unauthorized-error パスを指定して実行すると StatusCode 401 と共に以下のメッセージがクライアント側に返ります。 このように簡単に HTTP エラーを生成して返すことができます。
{"statusCode":401,"message":"Unauthorized"}
CORS
APIGatewayRestResolver のコンストラクタの CORSConfig クラスを利用した cors パラメータを通じて、CORS の設定を行うことができます。これによってパスに一致した関数が実行される時に、常にCORSヘッダーをレスポンスの一部として返すことを保証します。
Tips: cors=False とパラメータを指定することで、オプションで CORS を無効にすることができます。

レスポンス
この Lambda 関数に対して /products のパスで API Gateway を呼び出すと、次のレスポンスを API Gateway に返します。
{
"statusCode": 200,
"headers": {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Authorization,Content-Type,X-Amz-Date,X-Amz-Security-Token,X-Api-Key",
"Access-Control-Max-Age": "300"
},
"body": "{\"products\":[{\"id\":\"0001\",\"name\":\"M01\",\"price\":1000},{\"id\":\"0002\",\"name\":\"M02\",\"price\":2000},{\"id\":\"0003\",\"name\":\"M03\",\"price\":1500}]}",
"isBase64Encoded": false
}
クライアントへのレスポンス
API Gateway はこのレスポンスを以下のレスポインスヘッダへ変換してクライアントに返します。
{
"Access-Control-Allow-Headers": [
"Authorization,Content-Type,X-Amz-Date,X-Amz-Security-Token,X-Api-Key"
],
"Access-Control-Allow-Origin": ["*"],
"Access-Control-Max-Age": ["300"],
"Content-Type": ["application/json"],
"X-Amzn-Trace-Id": ["Root=1-62ef9c7b-9750387727c0f180c8db7007;Sampled=1"]
}
まとめ
いかがでしたでしょうか。今回は、AWS Lambda Powertools Python の EventHandler ユーティリティーの REST API について紹介しました。
Event Handler Utility を利用することで Amazon API Gateway や ALB からの Lambda 関数へのイベントのハンドルを容易にし、その実装も容易になることが理解できたかと思います。Amazon API Gateway の Lambda プロキシ統合や ALB と Lambda 関数の統合を実装する際には、この機能を組み込むことをお勧めします。次回は EventHandler の GraphQL API について紹介します。次回もお楽しみに。
筆者プロフィール
福井 厚
アマゾン ウェブ サービス ジャパン合同会社
シニアソリューションアーキテクト
Developerスペシャリスト - DevAx
2015 年からアマゾンウェブサービスジャパンでソリューションアーキテクトとして活動。最近は、Developer スペシャリスト SA として日々開発者の方にクラウドネイティブなモダンアプリケーション開発の技術支援を行なっています。

Did you find what you were looking for today?
Let us know so we can improve the quality of the content on our pages