API Gateway で Lambda プロキシ統合を設定する - Amazon API Gateway

API Gateway で Lambda プロキシ統合を設定する

API Gateway Lambda プロキシの統合について理解する

Amazon API Gateway Lambda プロキシ統合は、単一の API メソッドのセットアップで API を構築するシンプル、強力、高速なメカニズムです。Lambda プロキシ統合は、クライアントが単一の Lambda 関数をバックエンドで呼び出すことを可能にします。この関数は、他の Lambda 関数の呼び出しを含め、他の AWS のサービスのさまざまなリソースや機能にアクセスします。

Lambda プロキシ統合では、クライアントが API リクエストを送信すると、API Gateway は、統合された Lambda 関数にイベントオブジェクトを渡します。ただし、リクエストパラメータの順序は保持されません。このリクエストデータには、リクエストヘッダー、クエリ文字列パラメータ、URL パス変数、ペイロード、および API 設定データが含まれます。設定データには、現在のデプロイステージ名、ステージ変数、ユーザー ID、または承認コンテキスト (存在する場合) を含めることができます。バックエンド Lambda 関数では、受信リクエストデータを解析して、返すレスポンスを決定します。API Gateway が Lambda 出力を API レスポンスとしてクライアントに渡すには、Lambda 関数は結果をこの形式で返す必要があります。

API Gateway は Lambda プロキシ統合でクライアントとバックエンド Lambda 関数間にあまり介入しないため、クライアントと統合された Lambda 関数は、API の既存の 統合セットアップを損なうことなく、相互の変更に適応できます。これを有効にするには、クライアントはバックエンド Lambda 関数が制定したアプリケーションプロトコルに従う必要があります。

任意の API メソッドで Lambda プロキシ統合をセットアップできます。しかし、Lambda プロキシ統合は、汎用的なプロキシリソースが含まれる API メソッドに設定されていると、より強力です。汎用的なプロキシリソースは、特別なテンプレートパス変数である {proxy+} 、キャッチオールである ANY メソッドプレースホルダー、または両方によって示すことができます。クライアントは、受信リクエストのバックエンド Lambda 関数に、入力をリクエストパラメータまたは適切なペイロードとして渡すことができます。リクエストパラメータには、ヘッダー、URL パス変数、クエリ文字列パラメータ、および適切なペイロードが含まれます。統合された Lambda 関数は、リクエストを処理して、必要な入力がない場合に意味のあるエラーメッセージでクライアントに応答する前に、すべての入力ソースを検証します。

ANY の汎用的な HTTP メソッドと {proxy+} の汎用的なリソースと統合されている API メソッドを呼び出す際、クライアントは ANY の代わりに特定の HTTP メソッドを持つリクエストを送信します。クライアントはさらに、{proxy+} の代わりに特定の URL パスを指定し、要求されるヘッダー、クエリ文字列パラメータ、または適切なペイロードを含めます。

次のリストは、Lambda プロキシ統合での異なる API メソッドのランタイム動作を要約しています。

  • ANY /{proxy+}: クライアントは特定の HTTP メソッドを選択し、特定のリソースパス階層を設定する必要があり、Lambda 関数にデータを入力として渡すために任意のヘッダー、クエリ文字列パラメータ、および適切なペイロードを設定できます。

  • ANY /res: クライアントは特定の HTTP メソッドを選択する必要があり、Lambda 関数にデータを入力として渡すために任意のヘッダー、クエリ文字列パラメータ、および適切なペイロードを設定できます。

  • GET|POST|PUT|... /{proxy+}: クライアントは特定のリソースパス階層を設定することができ、Lambda 関数にデータを入力として渡すために任意のヘッダー、クエリ文字列パラメータ、および適切なペイロードを設定できます。

  • GET|POST|PUT|... /res/{path}/...: クライアントは ({path} 変数に) 特定のパスセグメントを選択する必要があり、Lambda 関数に入力データを渡すために任意のリクエストヘッダー、クエリ文字列パラメータ、および適切なペイロードを設定できます。

  • GET|POST|PUT|... /res: クライアントは Lambda 関数に入力データを渡すために任意のリクエストヘッダー、クエリ文字列パラメータ、および適切なペイロードを設定できます。

{proxy+} のプロキシリソースと {custom} のカスタムリソースの両方が、テンプレートパス変数として表現されています。ただし、{proxy+} はパス階層に沿った任意のリソースを参照できますが、{custom} は特定のパスセグメントのみを参照します。たとえば、食料品店はオンラインの製品インベントリを部門名、農産物カテゴリ、および製品タイプで整理しています。食料品店のウェブサイトは、カスタムリソースの次のテンプレートパス変数によって、利用可能な製品を表すことができます: /{department}/{produce-category}/{product-type}。たとえば、リンゴは /produce/fruit/apple で表され、ニンジンは /produce/vegetables/carrot で表されます。また、/{proxy+} を使用して、顧客がオンラインストアで買い物中に検索できる任意の部門、農産物カテゴリ、または製品タイプを表すことができます。たとえば、/{proxy+} は次のいずれかのアイテムを参照できます。

  • /produce

  • /produce/fruit

  • /produce/vegetables/carrot

顧客が利用可能な製品、その農産物カテゴリ、および関連するストア部門を検索できるようにするには、GET /{proxy+} の単一メソッドを読み取り専用アクセス権限で公開することができます。同様に、責任者が produce 部門のインベントリをアップデートできるようにするには、別の PUT /produce/{proxy+} の単一メソッドを読み取り/書き込みアクセス権限でセットアップできます。レジ係が野菜の合計を更新できるようにするには、POST /produce/vegetables/{proxy+} メッセージを読み取り/書き込みアクセス権限でセットアップできます。店長が任意のアクションを任意の利用可能な製品に実行できるようにするには、オンラインストア開発者は ANY /{proxy+} メソッドを読み取り/書き込みアクセス権限で公開できます。いずれの場合も、実行時には、顧客または従業員は選択された部門のあるタイプの特定の製品、製品部門の特定の農産物カテゴリ、または特定の部門を選択する必要があります。

API Gateway プロキシ統合のセットアップの詳細については、「プロキシリソースとのプロキシ統合を設定する」を参照してください。

プロキシ統合は、クライアントがバックエンド要件のより詳細な知識を持っていることを要求します。したがって、最適なアプリケーションパフォーマンスとユーザーエクスペリエンスを確保するために、バックエンド開発者はバックエンドの要件をクライアント開発者に明確に伝え、要件が満たされない場合は堅牢なエラーフィードバックメカニズムを提供する必要があります。

複数値のヘッダーとクエリ文字列パラメータのサポート

API Gateway は、同じ名前を持つ複数のヘッダーやクエリ文字列パラメータをサポートするようになりました。複数値ヘッダーや単一値ヘッダー、およびパラメータを、同じリクエストとレスポンスで組み合わせることができます。詳細については、プロキシ統合のための Lambda 関数の入力形式およびプロキシ統合のための Lambda 関数の出力形式を参照してください。

プロキシリソースに Lambda プロキシ統合をセットアップする

プロキシリソースに Lambda プロキシ統合タイプをセットアップするには、greedy パスパラメータ (/parent/{proxy+} など) を使用して API リソースを作成し、このリソースを arn:aws:lambda:us-west-2:123456789012:function:SimpleLambda4ProxyResource メソッドで Lambda 関数のバックエンド (ANY など) と統合します。greedy パスパラメータは、API リソースパスの末尾にある必要があります。プロキシ以外のリソースと同様に、API Gateway コンソールを使用するか、OpenAPI 定義ファイルをインポートするか、API Gateway REST API を直接呼び出すことによって、プロキシリソースをセットアップできます。

以下の OpenAPI API 定義ファイルは、SimpleLambda4ProxyResource という名前の Lambda 関数を使用して統合された API とプロキシリソースの例を示しています。

OpenAPI 3.0
{ "openapi": "3.0.0", "info": { "version": "2016-09-12T17:50:37Z", "title": "ProxyIntegrationWithLambda" }, "paths": { "/{proxy+}": { "x-amazon-apigateway-any-method": { "parameters": [ { "name": "proxy", "in": "path", "required": true, "schema": { "type": "string" } } ], "responses": {}, "x-amazon-apigateway-integration": { "responses": { "default": { "statusCode": "200" } }, "uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:SimpleLambda4ProxyResource/invocations", "passthroughBehavior": "when_no_match", "httpMethod": "POST", "cacheNamespace": "roq9wj", "cacheKeyParameters": [ "method.request.path.proxy" ], "type": "aws_proxy" } } } }, "servers": [ { "url": "https://gy415nuibc.execute-api.us-east-1.amazonaws.com/{basePath}", "variables": { "basePath": { "default": "/testStage" } } } ] }
OpenAPI 2.0
{ "swagger": "2.0", "info": { "version": "2016-09-12T17:50:37Z", "title": "ProxyIntegrationWithLambda" }, "host": "gy415nuibc.execute-api.us-east-1.amazonaws.com", "basePath": "/testStage", "schemes": [ "https" ], "paths": { "/{proxy+}": { "x-amazon-apigateway-any-method": { "produces": [ "application/json" ], "parameters": [ { "name": "proxy", "in": "path", "required": true, "type": "string" } ], "responses": {}, "x-amazon-apigateway-integration": { "responses": { "default": { "statusCode": "200" } }, "uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:SimpleLambda4ProxyResource/invocations", "passthroughBehavior": "when_no_match", "httpMethod": "POST", "cacheNamespace": "roq9wj", "cacheKeyParameters": [ "method.request.path.proxy" ], "type": "aws_proxy" } } } } }

Lambda プロキシ統合では、ランタイムに API Gateway は受信リクエストを Lambda 関数の入力 event パラメータにマッピングします。入力にはリクエストメソッド、パス、ヘッダー、クエリ文字列パラメータ、ペイロード、関連コンテキスト、定義済みステージ変数が含まれています。入力形式については「プロキシ統合のための Lambda 関数の入力形式」で説明しています。API Gateway が Lambda 出力を HTTP レスポンスに正常にマッピングするには、Lambda 関数は、「プロキシ統合のための Lambda 関数の出力形式」で説明されている形式で結果を出力する必要があります。

ANY メソッドによるプロキシリソースの Lambda プロキシ統合では、単一のバックエンド Lambda 関数が、プロキシリソースを通じてすべてのリクエストのイベントハンドラーの役割を果たします。たとえば、トラフィックパターンを記録するために、プロキシリソースの URL パスで /state/city/street/house を使用してリクエストを送信することによって、モバイルデバイスから州、市、番地、建物などの場所情報を送信できます。バックエンドの Lambda 関数は、URL パスを解析し、場所情報のタプルを DynamoDB テーブルに挿入できます。

AWS CLI を使用して Lambda プロキシ統合をセットアップする

このセクションでは、AWS CLI を使用して Lambda プロキシ統合で API をセットアップする方法について説明します。

注記

API Gateway コンソールを使用してプロキシリソースに Lambda プロキシ統合を設定する詳しい手順については、「チュートリアル: Lambda プロキシ統合を使用した Hello World REST API の構築」を参照してください。

たとえば、次のサンプル Lambda 関数を API のバックエンドとして使用します。

export const handler = function(event, context, callback) { console.log('Received event:', JSON.stringify(event, null, 2)); var res ={ "statusCode": 200, "headers": { "Content-Type": "*/*" } }; var greeter = 'World'; if (event.greeter && event.greeter!=="") { greeter = event.greeter; } else if (event.body && event.body !== "") { var body = JSON.parse(event.body); if (body.greeter && body.greeter !== "") { greeter = body.greeter; } } else if (event.queryStringParameters && event.queryStringParameters.greeter && event.queryStringParameters.greeter !== "") { greeter = event.queryStringParameters.greeter; } else if (event.multiValueHeaders && event.multiValueHeaders.greeter && event.multiValueHeaders.greeter != "") { greeter = event.multiValueHeaders.greeter.join(" and "); } else if (event.headers && event.headers.greeter && event.headers.greeter != "") { greeter = event.headers.greeter; } res.body = "Hello, " + greeter + "!"; callback(null, res); };

これをカスタムの Lambda 統合のセットアップと比較すると、この Lambda 関数への入力はリクエストパラメータと本文で表現できます。クライアントが同じ入力データを渡すことができるように、自由度は高くなっています。ここで、クライアントは、クエリ文字列パラメータ、ヘッダ、または本文プロパティとして Greeter の名前を渡すことができます。この関数は、カスタムの Lambda 統合をサポートすることもできます。API のセットアップはより簡単です。メソッドレスポンスまたは統合レスポンスを設定することはありません。

AWS CLI を使用して Lambda プロキシ統合をセットアップするには
  1. create-rest-api コマンドを呼び出して API を作成します。

    aws apigateway create-rest-api --name 'HelloWorld (AWS CLI)' --region us-west-2

    レスポンスの作成された API の id 値 (te6si5ach7) を書き留めます。

    { "name": "HelloWorldProxy (AWS CLI)", "id": "te6si5ach7", "createdDate": 1508461860 }

    このセクションでは、API id が必要です。

  2. get-resources コマンドを呼び出して、ルートリソース id を取得します。

    aws apigateway get-resources --rest-api-id te6si5ach7 --region us-west-2

    正常なレスポンスは次のように表示されます。

    { "items": [ { "path": "/", "id": "krznpq9xpg" } ] }

    ルートリソース id 値 (krznpq9xpg) を書き留めます。これは次のステップおよび後で必要になります。

  3. create-resource を呼び出して、/greeting の API Gateway リソースを作成します。

    aws apigateway create-resource --rest-api-id te6si5ach7 \ --region us-west-2 \ --parent-id krznpq9xpg \ --path-part {proxy+}

    正常に終了すると、レスポンスは以下のようになります。

    { "path": "/{proxy+}", "pathPart": "{proxy+}", "id": "2jf6xt", "parentId": "krznpq9xpg" }

    作成された {proxy+} リソースの id 値 (2jf6xt) を書き留めます。次のステップでは、/{proxy+} リソース上にメソッドを作成する必要があります。

  4. put-method を呼び出して、ANYANY /{proxy+} メソッドリクエストを作成します。

    aws apigateway put-method --rest-api-id te6si5ach7 \ --region us-west-2 \ --resource-id 2jf6xt \ --http-method ANY \ --authorization-type "NONE"

    正常に終了すると、レスポンスは以下のようになります。

    { "apiKeyRequired": false, "httpMethod": "ANY", "authorizationType": "NONE" }

    この API メソッドでは、クライアントはバックエンドの Lambda 関数からお知らせを受信または送信できます。

  5. put-integration を呼び出して、ANY /{proxy+} という名前の Lambda 関数で HelloWorld メソッドの統合をセットアップします。この関数は、"Hello, {name}!" パラメータが提供されている場合は greeter、クエリ文字列パラメータが設定されていない場合は "Hello, World!" のメッセージでリクエストに応答します。

    aws apigateway put-integration \ --region us-west-2 \ --rest-api-id te6si5ach7 \ --resource-id 2jf6xt \ --http-method ANY \ --type AWS_PROXY \ --integration-http-method POST \ --uri arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:123456789012:function:HelloWorld/invocations \ --credentials arn:aws:iam::123456789012:role/apigAwsProxyRole
    重要

    Lambda 統合では、関数呼び出しの Lambda サービスアクションの仕様に従って、統合リクエストに POST の HTTP メソッドを使用する必要があります。apigAwsProxyRole の IAM ロールは、apigateway サービスが Lambda 関数を呼び出せるようにするポリシーが必要です。IAM 許可の詳細については、「 API を呼び出すための API Gateway アクセス許可モデル」を参照してください。

    正しい出力は次の例のようになります。

    { "passthroughBehavior": "WHEN_NO_MATCH", "cacheKeyParameters": [], "uri": "arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:1234567890:function:HelloWorld/invocations", "httpMethod": "POST", "cacheNamespace": "vvom7n", "credentials": "arn:aws:iam::1234567890:role/apigAwsProxyRole", "type": "AWS_PROXY" }

    credentials の IAM ロールを指定する代わりに、add-permission コマンドを呼び出して、リソースベースのアクセス許可を追加することができます。これは、API Gateway コンソールが行うことです。

  6. create-deployment を呼び出して、API を test ステージにデプロイします。

    aws apigateway create-deployment --rest-api-id te6si5ach7 --stage-name test --region us-west-2
  7. ターミナルで次の cURL コマンドを使用して API をテストします。

    ?greeter=jane のクエリ文字列パラメータを使用して API を呼び出します。

    curl -X GET 'https://te6si5ach7.execute-api.us-west-2.amazonaws.com/test/greeting?greeter=jane'

    greeter:jane のヘッダーパラメータを使用して API を呼び出します。

    curl -X GET https://te6si5ach7.execute-api.us-west-2.amazonaws.com/test/hi \ -H 'content-type: application/json' \ -H 'greeter: jane'

    {"greeter":"jane"} の本文を使用して API を呼び出します。

    curl -X POST https://te6si5ach7.execute-api.us-west-2.amazonaws.com/test/hi \ -H 'content-type: application/json' \ -d '{ "greeter": "jane" }'

    すべてのケースで、出力は、次のレスポンス本文を持つ 200 レスポンスです。

    Hello, jane!

プロキシ統合のための Lambda 関数の入力形式

Lambda プロキシ統合では、API Gateway がクライアントリクエスト全体をバックエンド Lambda 関数の入力 event パラメータにマップします。次の例は、API Gateway が Lambda プロキシ統合に送信するイベントの構造を示しています。

{ "resource": "/my/path", "path": "/my/path", "httpMethod": "GET", "headers": { "header1": "value1", "header2": "value1,value2" }, "multiValueHeaders": { "header1": [ "value1" ], "header2": [ "value1", "value2" ] }, "queryStringParameters": { "parameter1": "value1,value2", "parameter2": "value" }, "multiValueQueryStringParameters": { "parameter1": [ "value1", "value2" ], "parameter2": [ "value" ] }, "requestContext": { "accountId": "123456789012", "apiId": "id", "authorizer": { "claims": null, "scopes": null }, "domainName": "id.execute-api.us-east-1.amazonaws.com", "domainPrefix": "id", "extendedRequestId": "request-id", "httpMethod": "GET", "identity": { "accessKey": null, "accountId": null, "caller": null, "cognitoAuthenticationProvider": null, "cognitoAuthenticationType": null, "cognitoIdentityId": null, "cognitoIdentityPoolId": null, "principalOrgId": null, "sourceIp": "IP", "user": null, "userAgent": "user-agent", "userArn": null, "clientCert": { "clientCertPem": "CERT_CONTENT", "subjectDN": "www.example.com", "issuerDN": "Example issuer", "serialNumber": "a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1", "validity": { "notBefore": "May 28 12:30:02 2019 GMT", "notAfter": "Aug 5 09:36:04 2021 GMT" } } }, "path": "/my/path", "protocol": "HTTP/1.1", "requestId": "id=", "requestTime": "04/Mar/2020:19:15:17 +0000", "requestTimeEpoch": 1583349317135, "resourceId": null, "resourcePath": "/my/path", "stage": "$default" }, "pathParameters": null, "stageVariables": null, "body": "Hello from Lambda!", "isBase64Encoded": false }
注記

入力値は、次のようになります。

  • headers キーには、単一値のヘッダーのみを含めることができます。

  • multiValueHeaders キーには、複数値のヘッダーや単一値のヘッダーを含めることができます。

  • headersmultiValueHeaders の両方の値を指定した場合、API Gateway はそれらを単一のリストにマージします。同じキーと値のペアが両方で指定された場合にのみ、multiValueHeaders の値が、マージされたリストに表示されます。

バックエンド Lambda 関数への入力では、requestContext オブジェクトはキーと値のペアのマップです。各ペアにおいて、キーは $context 変数プロパティの名前であり、値はそのプロパティの値です。API Gateway は、マップに新しいキーを追加することがあります。

有効になっている機能に応じて、requestContext マップは API ごとに異なる場合があります。たとえば、前述の例では、認証タイプが指定されていないため、$context.authorizer.*$context.identity.* プロパティは存在しません。認証タイプが指定されると、API Gateway は、認証されたユーザー情報を requestContext.identity オブジェクト内の統合エンドポイントに次のように渡します。

  • 認証タイプが AWS_IAM の場合、認証されるユーザー情報には $context.identity.* プロパティが含まれます。

  • 認証タイプが COGNITO_USER_POOLS (Amazon Cognito オーソライザー) の場合、認証されるユーザー情報には $context.identity.cognito* および $context.authorizer.claims.* プロパティが含まれます。

  • 認証タイプが CUSTOM (Lambda オーソライザー) の場合、認証されるユーザー情報には $context.authorizer.principalId およびその他の該当する $context.authorizer.* プロパティが含まれます。

プロキシ統合のための Lambda 関数の出力形式

Lambda プロキシ統合では、以下の JSON 形式に従って出力を返すために、API Gateway はバックエンドの Lambda 関数を必要とします。

{ "isBase64Encoded": true|false, "statusCode": httpStatusCode, "headers": { "headerName": "headerValue", ... }, "multiValueHeaders": { "headerName": ["headerValue", "headerValue2", ...], ... }, "body": "..." }

出力では、次のようになります。

  • 余分なレスポンスヘッダーが返されない場合、headers および multiValueHeaders キーは指定されません。

  • headers キーには、単一値のヘッダーのみを含めることができます。

  • multiValueHeaders キーには、複数値のヘッダーや単一値のヘッダーを含めることができます。multiValueHeaders キーを使用して、単一値のヘッダーを含めて、すべてのヘッダーを指定することができます。

  • headersmultiValueHeaders の両方の値を指定した場合、API Gateway はそれらを単一のリストにマージします。同じキーと値のペアが両方で指定された場合にのみ、multiValueHeaders の値が、マージされたリストに表示されます。

Lambda プロキシ統合に対して CORS を有効にするには、Access-Control-Allow-Origin:domain-name を出力 headers に追加する必要があります。domain-name は、任意のドメイン名に対して * にすることができます。出力 body は、メソッドレスポンスペイロードとしてフロントエンドにマーシャリングされます。body がバイナリ BLOB の場合、isBase64Encodedtrue に設定し、*/* を [バイナリメディアタイプ] に設定することで、Base64 エンコード文字列としてエンコードできます。それ以外の場合は、false に設定するか、指定しないでおくことができます。

注記

バイナリサポートの有効化に関する詳細については、API Gateway コンソールを使用したバイナリサポートの有効化 を参照してください。サンプルの Lambda 関数については、「Lambda プロキシ統合からバイナリメディアを返す」を参照してください。

関数出力が別の形式である場合、API Gateway は 502 Bad Gateway エラーレスポンスを返します。

Node.js での Lambda 関数でレスポンスを返すには、次のようなコマンドを使用できます。

  • 成功した場合の結果を返すには、callback(null, {"statusCode": 200, "body": "results"}) を呼び出します。

  • 例外をスローするには、callback(new Error('internal server error')) を呼び出します。

  • クライアント側エラーの場合 (必要なパラメータがない場合など)、callback(null, {"statusCode": 400, "body": "Missing parameters of ..."}) を呼び出して、例外をスローせずにエラーを返すことができます。

Node.js の Lambda async 関数では、同等の構文は次のようになります。

  • 成功した場合の結果を返すには、return {"statusCode": 200, "body": "results"} を呼び出します。

  • 例外をスローするには、throw new Error("internal server error") を呼び出します。

  • クライアント側エラーの場合 (必要なパラメータがない場合など)、return {"statusCode": 400, "body": "Missing parameters of ..."} を呼び出して、例外をスローせずにエラーを返すことができます。