AWS SAMテンプレートでREST APIの複数メソッドをひとつのLambdaに統合するには?
photo credit: Andreas Komodromos Urban Nights - Hudson Yards, New York City via photopin (license)
ホントちょっとしたことなんですが、SAMのHelloWorldのチュートリアルをやっただけではハッキリわからなくて、少しだけ試行錯誤が必要でしたので書いておきます。
API Gateway REST APIの複数メソッド(GET,PUT,POST,DELETEなど)を、ひとつのLambda関数に統合(Lambdaプロキシ統合)する場合の AWS SAMのテンプレートの書き方です。
API Gatewayのコンソールでは、各メソッドの設定で、同じLambda関数を指定するだけですが、じゃあSAMのテンプレートではどう書くのでしょうか?ってところです。
SAMテンプレートの書き方(例)
以下のYamlでリソースパス /hello
に対する GETリクエストとPUTリクエストを、Lambda関数 HelloWorldFunction
に統合しています。
sam-app/template.yaml:
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > sam-app Sample SAM Template for sam-app # More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst Globals: Function: Timeout: 3 Resources: HelloWorldFunction: Type: AWS::Serverless::Function Properties: Events: #ココ↓と HelloWorldGet: Type: Api Properties: Path: /hello Method: get #ココ↓ HelloWorldPut: Type: Api Properties: Path: /hello Method: put
一般化すると、AWS::Serverless::Function
のProperties/Events
以下にメソッド単位で別々のApiとして宣言すればよいのです。
APIのリソースパスが同じでもメソッドが別なら複数のApi定義が必要ということですね。
SAMテンプレートでは独立したREST APIを宣言しているのではなくて、あくまでもサーバーレス関数の定義の一部なのでこうなるのでしょう(多分)。
今となっては上の定義が理解できていますが、当初「Events 以下のエントリがAPIのリソースパスを一意に表している」と思い込んでいて、Methodを配列にしてみたりして失敗しました。
Lambdaプロキシ統合のパラメータで分岐する
Lambda関数側では、引数のeventのhttpMethod
にHTTPメソッドが文字列で格納されていますので、これによって分岐します。
exports.lambdaHandler = async (event) => { try { const response = { statusCode: 200 }; switch(event.httpMethod) { case "PUT": await put(JSON.parse(event.body)); break; case "GET": await get(event.queryStringParameters); break; } return response; } catch (err) { console.log(err); return err; } };
リクエストパラメータは、同じくeventのqueryStringParameter
やbody
に格納されています。
queryStringParameter
はObjectですがbodyは文字列(JSON)なので気を付けましょう。
これらパラメータの使い方はメソッドの設計に依存するので一概に「こうしましょう」とは言えません。
他にも pathParameters
やheaders
を利用することがあるかもしれませんね。
Lambdaプロキシ統合されたLambda関数の入力形式については、以下の公式ページに説明があります。 実際に動かしてみて把握する必要はあるかもしれませんよ。
あとがき
普通はメソッドごとにLambda関数を分けることが多いかもしれません。 そのほうがLambdaでの分岐が不要で多少はパフォーマンスが良いはずですし。
しかし、各メソッド間で同じデータモデルを扱うなど共通処理が必要な場合は一つにまとめるとスッキリすると思います。
共通処理についてはLambda Layersを使うのも一つの解です。 ここでは、「とりあえず手軽にやるには?」という観点でサクッと調べた結果を書いておきました。