Menu
Amazon API Gateway
Developer Guide

Set up Lambda Proxy Integrations in API Gateway

Understand the API Gateway Lambda Proxy Integration

The Lambda proxy integration of API Gateway is a simple, powerful, and nimble mechanism to build an API with a setup of a single API method. The Lambda proxy integration allows the client to call a single Lambda function in the backend. The function accesses many resources or features of other AWS services, including calling other Lambda functions.

With the Lambda proxy integration, when a client submits an API request, API Gateway passes to the integrated Lambda function the raw request as-is. This request data includes the request headers, query string parameters, URL path variables, payload, and API configuration data. The configuration data can include current deployment stage name, stage variables, user identity, or authorization context (if any). The backend Lambda function parses the incoming request data to determine the response that it returns. For API Gateway to pass the Lambda output as the API response to the client, the Lambda function must return the result in this format.

Because API Gateway doesn't intervene very much between the client and the backend Lambda function for the Lambda proxy integration, the client and the integrated Lambda function can adapt to changes in each other without breaking the existing integration setup of the API. To enable this, the client must follow application protocols enacted by the backend Lambda function.

You can set up a Lambda proxy integration for any API method. But a Lambda proxy integration is more potent when it is configured for an API method involving a generic proxy resource. The generic proxy resource can be denoted by a special templated path variable of {proxy+}, the catch-all ANY method placeholder, or both. The client can pass the input to the backend Lambda function in the incoming request as request parameters or applicable payload. The request parameters include headers, URL path variables, query string parameters, and the applicable payload. The integrated Lambda function verifies all of the input sources before processing the request and responding to the client with meaningful error messages if any of the required input is missing.

When calling an API method integrated with the generic HTTP method of ANY and the generic resource of {proxy+}, the client submits a request with a particular HTTP method in place of ANY. The client also specifies a particular URL path instead of {proxy+}, and includes any required headers, query string parameters, or an applicable payload.

The following list summarizes runtime behaviors of different API methods with the Lambda proxy integration:

  • ANY /{proxy+}: The client must choose a particular HTTP method, must set a particular resource path hierarchy, and can set any headers, query string parameters, and applicable payload to pass the data as input to the integrated Lambda function.

  • ANY /res: The client must choose a particular HTTP method and can set any headers, query string parameters, and applicable payload to pass the data as input to the integrated Lambda function.

  • GET|POST|PUT|... /{proxy+}: The client can set a particular resource path hierarchy, any headers, query string parameters, and applicable payload to pass the data as input to the integrated Lambda function.

  • GET|POST|PUT|... /res/{path}/...: The client must choose a particular path segment (for the {path} variable) and can set any request headers, query string parameters, and applicable payload to pass input data to the integrated Lambda function.

  • GET|POST|PUT|... /res: The client can choose any request headers, query string parameters, and applicable payload to pass input data to the integrated Lambda function.

Both the proxy resource of {proxy+} and the custom resource of {custom} are expressed as templated path variables. However {proxy+} can refer to any resource along a path hierarchy, while {custom} refers to a particular path segment only. For example, a grocery store might organize its online product inventory by department names, produce categories, and product types. The grocery store's website can then represent available products by the following templated path variables of custom resources: /{department}/{produce-category}/{product-type}. For example, apples are represented by /produce/fruit/apple and carrots by /produce/vegetables/carrot. It can also use /{proxy+} to represent any department, any produce category, or any product type that a customer can search for while shopping in the online store. For example, /{proxy+} can refer to any of the following items:

  • /produce

  • /produce/fruit

  • /produce/vegetables/carrot

To let customers search for any available product, its produce category, and the associated store department, you can expose a single method of GET /{proxy+} with read-only permissions. Similarly, to allow a supervisor to update the produce department's inventory, you can set up another single method of PUT /produce/{proxy+} with read/write permissions. To allow a cashier to update the running total of a vegetable, you can set up a POST /produce/vegetables/{proxy+} method with read/write permissions. To let a store manager perform any possible action on any available product, the online store developer can expose the ANY /{proxy+} method with read/write permissions. In any case, at run time, the customer or the employee must select a particular product of a given type in a chosen department, a specific produce category in a chosen department, or a specific department.

For more information about setting up the API Gateway proxy integrations, see Set up a Proxy Integration with a Proxy Resource.

The proxy integration requires that the client have more detailed knowledge of the backend requirements. Therefore, to ensure optimal app performance and user experience, the backend developer must communicate clearly to the client developer the requirements of the backend, and provide a robust error feedback mechanism when the requirements are not met.

Set up a Proxy Resource with the Lambda Proxy Integration

To set up a proxy resource with the Lambda proxy integration type, create an API resource with a greedy path parameter (for example, /parent/{proxy+}) and integrate this resource with a Lambda function backend (for example, arn:aws:lambda:us-west-2:123456789012:function:SimpleLambda4ProxyResource) on the ANY method. The greedy path parameter must be at the end of the API resource path. As with a non-proxy resource, you can set up the proxy resource by using the API Gateway console, importing a Swagger definition file, or calling the API Gateway REST API directly.

For detailed instructions about using the API Gateway console to configure a proxy resource with the Lambda proxy integration, see Build an API Gateway API with Lambda Proxy Integration .

The following Swagger API definition file shows an example of an API with a proxy resource that is integrated with the SimpleLambda4ProxyResource Lambda function.

Copy
{ "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" } } } } }

With the Lambda proxy integration, at run time, API Gateway maps an incoming request into the input event parameter of the Lambda function. The input includes the request method, path, headers, any query parameters, any payload, associated context, and any defined stage variables. The input format is explained in Input Format of a Lambda Function for Proxy Integration . For API Gateway to map the Lambda output to HTTP responses successfully, the Lambda function must output the result in the format described in Output Format of a Lambda Function for Proxy Integration.

With the Lambda proxy integration of a proxy resource through the ANY method, the single backend Lambda function serves as the event handler for all requests through the proxy resource. For example, to log traffic patterns, you can have a mobile device send its location information of state, city, street, and building by submitting a request with /state/city/street/house in the URL path for the proxy resource. The backend Lambda function can then parse the URL path and insert the location tuples into a DynamoDB table.

Set up Lambda Proxy Integration Using the AWS CLI

In this section, we show how to use AWS CLI to set up an API with the Lambda proxy integration.

As an example, we use the following sample Lambda function as the backend of the API:

Copy
exports.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.headers && event.headers.greeter && event.headers.greeter != "") { greeter = event.headers.greeter; } res.body = "Hello, " + greeter + "!"; callback(null, res); };

Comparing this to the Lambda custom integration setup, the input to this Lambda function can be expressed in the request parameters and body. You have more latitude to allow the client to pass the same input data. Here, the client can pass the greeter's name in as a query string parameter, a header, or a body property. The function can also support the Lambda custom integration. The API setup is simpler. You do not configure the method response or integration response at all.

To set up a Lambda proxy integration using the AWS CLI

  1. Call the create-rest-api command to create an API:

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

    Note the resulting API's id value (te6si5ach7) in the response:

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

    You need the API id throughout this section.

  2. Call the get-resources command to get the root resource id:

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

    The successful response is shown as follows:

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

    Note the root resource id value (krznpq9xpg). You need it in the next step and later.

  3. Call create-resource to create an API Gateway Resource of /greeting:

    Copy
    aws apigateway create-resource --rest-api-id te6si5ach7 \ --region us-west-2 \ --parent-id krznpq9xpg \ --path-part greeting

    The successful response is similar to the following:

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

    Note the resulting {proxy+} resource's id value (2jf6xt). You need it to create a method on the /{proxy+} resource in the next step.

  4. Call put-method to create an ANY method request of ANY /{proxy+}:

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

    The successful response is similar to the following:

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

    This API method allows the client to receive or send greetings from the Lambda function at the backend.

  5. Call put-integration to set up the integration of the ANY /{proxy+} method with a Lambda function, named HelloWorld. This function responds to the request with a message of "Hello, {name}!", if the greeter parameter is provided, or "Hello, World!", if the query string parameter is not set.

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

    For Lambda integrations, you must use the HTTP method of POST for the integration request. The IAM role of apigAwsProxyRole must have policies allowing the apigateway service to invoke Lambda functions. For more information about the IAM permissions, see API Gateway Permissions Model for Creating and Managing an API.

    The successful output is similar to the following:

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

    Instead of supplying an IAM role for credentials, you can call the add-permission command to add resource-based permissions. This is what the API Gateway console does.

  6. Call create-deployment to deploy the API to a test stage:

    Copy
    aws apigateway create-deployment --rest-api-id te6si5ach7 --stage-name test
  7. Test the API using the following cURL commands in a terminal.

    Calling the API with the query string parameter of ?greeter=jane:

    Copy
    curl -X GET 'https://te6si5ach7.execute-api.us-west-2.amazonaws.com/test/greeting?greeter=jane' \ -H 'authorization: AWS4-HMAC-SHA256 Credential={access_key}/20171020/us-west-2/execute-api/aws4_request, \ SignedHeaders=content-type;host;x-amz-date, Signature=f327...5751'

    Calling the API with a header parameter of greeter:jane:

    Copy
    curl -X GET https://te6si5ach7.execute-api.us-west-2.amazonaws.com/test/hi \ -H 'authorization: AWS4-HMAC-SHA256 Credential={access_key}/20171020/us-west-2/execute-api/aws4_request, \ SignedHeaders=content-type;host;x-amz-date, Signature=f327...5751' \ -H 'content-type: application/json' \ -H 'greeter: jane'

    Calling the API with a body of {"greeter":"jane"}:

    Copy
    curl -X POST https://te6si5ach7.execute-api.us-west-2.amazonaws.com/test \ -H 'authorization: AWS4-HMAC-SHA256 Credential={access_key}/20171020/us-west-2/execute-api/aws4_request, \ SignedHeaders=content-type;host;x-amz-date, Signature=f327...5751' -H 'content-type: application/json' \ -d '{ "greeter": "jane" }'

    In all the cases, the output is a 200 response with the following response body:

    Copy
    Hello, jane!

Input Format of a Lambda Function for Proxy Integration

With the Lambda proxy integration, API Gateway maps the entire client request to the input event parameter of the backend Lambda function as follows:

Copy
{ "resource": "Resource path", "path": "Path parameter", "httpMethod": "Incoming request's method name" "headers": {Incoming request headers} "queryStringParameters": {query string parameters } "pathParameters": {path parameters} "stageVariables": {Applicable stage variables} "requestContext": {Request context, including authorizer-returned key-value pairs} "body": "A JSON string of the request payload." "isBase64Encoded": "A boolean flag to indicate if the applicable request payload is Base64-encode" }

We illustrate this using the following POST request to show an API deployed to testStage with a stage variable of stageVariableName=stageVariableValue:

Copy
POST /testStage/hello/world?name=me HTTP/1.1 Host: gy415nuibc.execute-api.us-east-1.amazonaws.com Content-Type: application/json headerName: headerValue { "a": 1 }

This request produces the following response payload, which contains the output returned from the backend Lambda function, where input was set to the event parameter to the Lambda function.

Copy
{ "message": "Hello me!", "input": { "resource": "/{proxy+}", "path": "/hello/world", "httpMethod": "POST", "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "cache-control": "no-cache", "CloudFront-Forwarded-Proto": "https", "CloudFront-Is-Desktop-Viewer": "true", "CloudFront-Is-Mobile-Viewer": "false", "CloudFront-Is-SmartTV-Viewer": "false", "CloudFront-Is-Tablet-Viewer": "false", "CloudFront-Viewer-Country": "US", "Content-Type": "application/json", "headerName": "headerValue", "Host": "gy415nuibc.execute-api.us-east-1.amazonaws.com", "Postman-Token": "9f583ef0-ed83-4a38-aef3-eb9ce3f7a57f", "User-Agent": "PostmanRuntime/2.4.5", "Via": "1.1 d98420743a69852491bbdea73f7680bd.cloudfront.net (CloudFront)", "X-Amz-Cf-Id": "pn-PWIJc6thYnZm5P0NMgOUglL1DYtl0gdeJky8tqsg8iS_sgsKD1A==", "X-Forwarded-For": "54.240.196.186, 54.182.214.83", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https" }, "queryStringParameters": { "name": "me" }, "pathParameters": { "proxy": "hello/world" }, "stageVariables": { "stageVariableName": "stageVariableValue" }, "requestContext": { "accountId": "12345678912", "resourceId": "roq9wj", "stage": "testStage", "requestId": "deef4878-7910-11e6-8f14-25afc3e9ae33", "identity": { "cognitoIdentityPoolId": null, "accountId": null, "cognitoIdentityId": null, "caller": null, "apiKey": null, "sourceIp": "192.168.196.186", "cognitoAuthenticationType": null, "cognitoAuthenticationProvider": null, "userArn": null, "userAgent": "PostmanRuntime/2.4.5", "user": null }, "resourcePath": "/{proxy+}", "httpMethod": "POST", "apiId": "gy415nuibc" }, "body": "{\r\n\t\"a\": 1\r\n}", "isBase64Encoded": false } }

In the input to Lambda, the requestContext object is a map of key-value pairs. The key is a property name of the $context variable and the value is the property value of the corresponding $context variable. API Gateway may add new keys to the map. Depending on the features enabled, the requestContext map may vary from API to API. For example, in the preceding example, $context.authorizer.* properties are absent because no custom authorizer is enabled for the API.

Note

API Gateway enacts certain restrictions and limitations when handling methods with either Lambda proxy integration or HTTP proxy integration. For details, see Known Issues.

Output Format of a Lambda Function for Proxy Integration

With the Lambda proxy integration, API Gateway requires the backend Lambda function to return output according to the following JSON format:

Copy
{ "isBase64Encoded": true|false, "statusCode": httpStatusCode, "headers": { "headerName": "headerValue", ... }, "body": "..." }

In the output, headers can be unspecified if no extra response headers are to be returned. To enable CORS for the Lambda proxy integration, you must add Access-Control-Allow-Origin:domain-name to the output headers. domain-name can be * for any domain name. The output body is marshalled to the frontend as the method response payload. If body is a binary blob, you can encode it as a Base64-encoded string and set isBase64Encoded to true. Otherwise, you can set it to false or leave it unspecified.

If the function output is of a different format, API Gateway returns a 502 Bad Gateway error response.

In a Lambda function in Node.js, to return a successful response, call callback(null, {"statusCode": 200, "body": "results"}). To throw an exception, call callback(new Error('internal server error')). For a client-side error (if, for example, a required parameter is missing), you can call callback(null, {"statusCode": 400, "body": "Missing parameters of ..."}) to return the error without throwing an exception.