How do I use API Gateway as a proxy for another AWS service?

11 minute read
2

I want to use Amazon API Gateway as a proxy for another AWS service and integrate other services with API Gateway.

Short description

AWS service APIs are essentially REST APIs that you can make an HTTPS request to. You can integrate many AWS services with API Gateway, but the setup and mapping vary based on the particular service API.

To integrate another service with API Gateway, build an HTTPS request from API Gateway to the service API. This way, all request parameters are correctly mapped.

This article describes an example setup for integrating the Amazon Simple Notification Service (Amazon SNS) Publish API with API Gateway. Use this example as an outline for integrating other services.

Resolution

Note: If you receive errors when running AWS Command Line Interface (AWS CLI) commands, make sure that you're using the most recent AWS CLI version.

Set up the required tools and resources

Configure your environment and create all the AWS resources required for your use case. For the Amazon SNS example setup, do the following:

  1. Install the AWS CLI.
  2. Create an Amazon SNS topic. Note the topic's Amazon Resource Name (ARN). Use this information in the next step, and later in this setup.
  3. Create a subscription to the topic.
  4. Open the AWS Identity and Access Management (IAM) console and then create an AWS service proxy execution role. Note the role's ARN for later in the setup. This IAM role gives API Gateway permissions as a trusted entity to assume the service and perform the API action that you're integrating. For the Amazon SNS example setup, allow the action sns:Publish. For more information, see API Gateway permissions model for invoking an API.
  5. Create an API Gateway REST API with a test resource. For more information and examples, see Amazon API Gateway tutorials and workshops.
    Note: Optionally, import the REST API using the following sample OpenAPI 2.0 (Swagger) definition. This option preconfigures the settings for the Amazon SNS example setup. Be sure to replace arn:aws:iam::account-id:role/apigateway-sns-role with your IAM role's ARN. Replace region with the AWS Region where you want to create your REST API.
{
  "swagger": "2.0",
  "info": {
    "version": "2019-10-09T14:10:24Z",
    "title": "aws-service-integration"
  },
  "basePath": "/dev",
  "schemes": [
    "https"
  ],
  "paths": {
    "/test": {
      "post": {
        "produces": [
          "application/json"
        ],
        "parameters": [
          {
            "name": "Message",
            "in": "query",
            "required": true,
            "type": "string"
          },
          {
            "name": "TopicArn",
            "in": "query",
            "required": true,
            "type": "string"
          }
        ],
        "responses": {
          "200": {
            "description": "200 response",
            "schema": {
              "$ref": "#/definitions/Empty"
            }
          }
        },
        "x-amazon-apigateway-integration": {
          "credentials": "arn:aws:iam::account-id:role/apigateway-sns-role",
          "uri": "arn:aws:apigateway:region:sns:action/Publish",
          "responses": {
            "default": {
              "statusCode": "200"
            }
          },
          "requestParameters": {
            "integration.request.querystring.TopicArn": "method.request.querystring.TopicArn",
            "integration.request.querystring.Message": "method.request.querystring.Message"
          },
          "passthroughBehavior": "when_no_match",
          "httpMethod": "POST",
          "type": "aws"
        }
      }
    }
  },
  "definitions": {
    "Empty": {
      "type": "object",
      "title": "Empty Schema"
    }
  }
}

Get an example HTTPS request

An example HTTPS request from the service API that you're integrating can help you correctly map the request parameters in API Gateway. To get an example HTTPS request, do one of the following:

Check for examples in the API documentation. For the Amazon SNS Publish API, you can refer to the service's API Reference for an example request:

https://sns.us-west-2.amazonaws.com/?Action=Publish
&TargetArn=arn%3Aaws%3Asns%3Aus-west-2%3A803981987763%3Aendpoint%2FAPNS_SANDBOX%2Fpushapp%2F98e9ced9-f136-3893-9d60-776547eafebb
&Message=%7B%22default%22%3A%22This+is+the+default+Message%22%2C%22APNS_SANDBOX%22%3A%22%7B+%5C%22aps%5C%22+%3A+%7B+%5C%22alert%5C%22+%3A+%5C%22You+have+got+email.%5C%22%2C+%5C%22badge%5C%22+%3A+9%2C%5C%22sound%5C%22+%3A%5C%22default%5C%22%7D%7D%22%7D
&Version=2010-03-31
&AUTHPARAMS

- or -

Generate it from an API call. Use the AWS CLI to call the service API, and then analyze the output. Determine the corresponding AWS CLI command for the service API that you're integrating, and then run a test request with the --debug option.

Tip: Check the AWS CLI Command Reference to find the corresponding AWS CLI command.

For the Amazon SNS example setup, run this command:

Note: Replace arn:aws:sns:us-east-1:123456789012:test with your Amazon SNS topic's ARN.

$ aws sns publish --topic-arn arn:aws:sns:us-east-1:123456789012:test --message "hi" --debug

The command output contains the HTTPS request and the headers that are passed. Here's an example of what to look for:

2018-11-22 11:56:39,647 - MainThread - botocore.client - DEBUG - Registering retry handlers for service: sns
2018-11-22 11:56:39,648 - MainThread - botocore.hooks - DEBUG - Event before-parameter-build.sns.Publish: calling handler <function generate_idempotent_uuid at 0x11093d320>
2018-11-22 11:56:39,649 - MainThread - botocore.endpoint - DEBUG - Making request for OperationModel(name=Publish) (verify_ssl=True) with params: {'body': {'Action': u'Publish', u'Message': u'hello', 'Version': u'2010-03-31', u'TopicArn': u'arn:aws:sns:us-east-1:123456789012:test'}, 'url': u'https://sns.us-east-1.amazonaws.com/', 'headers': {'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8', 'User-Agent': 'aws-cli/1.15.74 Python/2.7.14 Darwin/16.7.0 botocore/1.9.23'}, 'context': {'auth_type': None, 'client_region': 'us-east-1', 'has_streaming_input': False, 'client_config': <botocore.config.Config object at 0x1118437d0>}, 'query_string': '', 'url_path': '/', 'method': u'POST'}
2018-11-22 11:56:39,650 - MainThread - botocore.hooks - DEBUG - Event request-created.sns.Publish: calling handler <bound method RequestSigner.handler of <botocore.signers.RequestSigner object at 0x111843750>>
2018-11-22 11:56:39,650 - MainThread - botocore.hooks - DEBUG - Event choose-signer.sns.Publish: calling handler <function set_operation_specific_signer at 0x11093d230>
2018-11-22 11:56:39,650 - MainThread - botocore.auth - DEBUG - Calculating signature using v4 auth.
2018-11-22 11:56:39,651 - MainThread - botocore.auth - DEBUG - CanonicalRequest:
POST
/

content-type:application/x-www-form-urlencoded; charset=utf-8
host:sns.us-east-1.amazonaws.com
x-amz-date:20181122T062639Z

content-type;host;x-amz-date

In this example, the request is sent as a POST HTTP method.

Create a method for your API Gateway API

  1. In the API Gateway console, on the APIs pane, choose the name of your API.
  2. In the Resources pane, choose a resource. For the Amazon SNS example setup, choose the test resource that you created.
  3. Choose Actions, and then choose Create Method.
  4. In the dropdown list, choose the method used by your service API in the example HTTPS request. (For the Amazon SNS example setup, choose POST.) Then, choose the check mark icon.
  5. On the Setup pane, do the following:
    For Integration type, choose AWS Service.
    For AWS Region, choose the AWS Region of the resource associated with the service API that you're integrating. For the Amazon SNS example setup, choose the Region of your SNS topic.
    For AWS Service, choose the service that you're integrating with API Gateway. For example, Simple Notification Service (SNS).
    (Optional) For AWS Subdomain, enter the subdomain used by the AWS service. Check the service's documentation to confirm the availability of a subdomain. For the Amazon SNS example setup, leave it blank.
    For HTTP method, choose the method that corresponds to the AWS service API that you're integrating. For the Amazon SNS example setup, choose POST.
    For Action Type, if the service API that you're integrating is a supported action, choose Use action name. Check the service's API reference for a list of supported actions. For Amazon SNS, see Actions.
    For Action, enter the name of the service API. For the Amazon SNS example setup, enter Publish.
    -or-
    For Action Type, if the AWS service API expects a resource path in your request, choose Use path override. For example, for the Amazon Polly ListLexicons API, enter /v1/lexicons for Path override (optional).
    For Execution role, enter the ARN of the IAM role that you created.
    (Optional) For Content Handling and Use Default Timeout, make changes as needed for your use case. For the Amazon SNS example setup, don't change these settings.
  6. Choose Save.

Create parameters for the method request

Determine the required and optional request parameters for the service API that you're integrating. To identify these parameters, refer to the example HTTPS request that you got earlier, or refer to the API Reference for the service API. For example, see Publish.

  1. In the API Gateway console, on the Method Execution pane for your API Gateway API's method, choose Method Request.
  2. (Optional) On the Method Request pane, for Request Validator, choose a request validator, body, and headers if you want to validate the query string parameters.
  3. Expand URL Query String Parameters.
  4. Choose Add query string.
  5. For Name, enter the name of a request parameter for the service API that you're integrating.
  6. Choose the check mark icon (Create a new query string).
  7. If the parameter is required, select the check box under Required.
  8. Repeat steps 4-7 for all request parameters that you want to include. For the Amazon SNS example setup, create a parameter named T****opicArn and another named Message.

For more information, see Set up a method using the API Gateway console.

Note: For some service APIs, you must send required headers and a body in the integration request in addition to the required parameters. You can create the headers and body on the Integration Request pane under HTTP Request Headers and Request Body.

For example, if you're integrating the Amazon Rekognition ListCollections API, create the header X-Amz-Target: RekognitionService.ListCollections. The request looks like this:

POST https://rekognition.us-west-2.amazonaws.com/ HTTP/1.1
    Host: rekognition.us-west-2.amazonaws.com
    Accept-Encoding: identity
    Content-Length: 2
    X-Amz-Target: RekognitionService.ListCollections
    X-Amz-Date: 20170105T155800Z
    User-Agent: aws-cli/1.11.25 Python/2.7.9 Windows/8 botocore/1.4.82
    Content-Type: application/x-amz-json-1.1
    Authorization: AWS4-HMAC-SHA256 Credential=XXXXXXXX/20170105/us-west-2/rekognition/aws4_request,
      SignedHeaders=content-type;host;x-amz-date;x-amz-target, Signature=XXXXXXXX

    {}

If you're integrating the Amazon Simple Queue Service (Amazon SQS) SendMessage API, map the request body using the mapping expression method.request.body.JSONPath_EXPRESSION. (Replace JSONPath_EXPRESSION with a JSONPath expression for a JSON field of the body of the request.) In this example, a request looks similar to the following:

{'url_path': '/', 'query_string': '', 'method': 'POST',
'headers': {'Content-Type': 'application/x-www-form-urlencoded; 
charset=utf-8', 'User-Agent': 'aws-cli/1.16.81 Python/3.6.5 Darwin/18.7.0 botocore/1.12.183'}, 
'body': {'Action': 'SendMessage', 'Version': '2012-11-05', 'QueueUrl': 'https://sqs.ap-southeast-2.amazonaws.com/123456789012/test01', 'MessageBody': 'Hello'}, 
'url': 'https://ap-southeast-2.queue.amazonaws.com/', 'context': {'client_region': 'ap-southeast-2', 'client_config': <botocore.config.Config object at 0x106862da0>, 'has_streaming_input': False, 'auth_type': None}}

Create parameters for the integration request

Map the parameters that you created for the method request to parameters for the integration request.

  1. In the API Gateway console, go back to the Method Execution pane for your API Gateway API's method, and then choose Integration Request.
  2. On the Integration Request pane, expand URL Query String Parameters.
  3. Choose Add query string.
  4. For Name, enter the name of a request parameter for the service API that you're integrating.
    Note: The name is case-sensitive and must appear exactly as expected by the service API.
  5. For Mapped from, enter method.request.querystring.param_name. Replace param_name with the name of the corresponding parameter that you created for the method request. For example, method.request.querystring.TopicArn.
  6. Choose the check mark icon (Create).
  7. Repeat steps 3-6 to create parameters for the integration request that correspond to each of the parameters that you created for the method request.

Note: If you created required headers and a body for the method request, map them to the integration request, too. Create them on the Integration Request pane under HTTP Headers and Mapping Templates.

For more information, see Set up an API integration request using the API Gateway console.

(Optional) Check your integration configuration

To confirm that your integration setup looks as you expect, you can run the AWS CLI get-integration command to check the configuration similar to the following:

$ aws apigateway get-integration --rest-api-id 1234123412 --resource-id y9h6rt --http-method POST

For the Amazon SNS example setup, the output looks similar to the following:

{
    "integrationResponses": {
        "200": {
            "responseTemplates": {
                "application/json": null
            },
            "statusCode": "200"
        }
    },
    "passthroughBehavior": "WHEN_NO_MATCH",
    "timeoutInMillis": 29000,
    "uri": "arn:aws:apigateway:us-east-2:sns:action/Publish",
    "httpMethod": "POST",
    "cacheNamespace": "y9h6rt",
    "credentials": "arn:aws:iam::1234567890:role/apigateway-sns-role",
    "type": "AWS",
    "requestParameters": {
        "integration.request.querystring.TopicArn": "method.request.querystring.TopicArn",
        "integration.request.querystring.Message": "method.request.querystring.Message"
    },
    "cacheKeyParameters": []
}

In the API Gateway console, go back to the Method Execution pane for your API Gateway API's method, and then choose TEST.

  1. On the Method Test pane, do the following:
    For Query Strings, enter a query string that includes request parameters and values for them. For the Amazon SNS example setup, enter TopicArn= arn:aws:sns:us-east-1:123456789012:test&Message="Hello". Replace arn:aws:sns:us-east-1:123456789012:test with your Amazon SNS topic's ARN.
    For Headers and Request Body, if you created these for your setup, enter the header names and request body JSON.
    Choose Test. A response appears in the Method Test pane. If the response is successful, you see Status: 200. For the Amazon SNS example setup, a successful response includes a MessageId in the response body.
    For more information, see Use the API Gateway console to test a REST API method.
  2. Deploy your REST API.
  3. Test your API using any tool that you prefer.

Related information

Tutorial: Build an API Gateway REST API with AWS integration

Set up REST API methods in API Gateway

Setting up REST API integrations

Set up request and response data mappings using the API Gateway console

2 Comments

Please note to anyone using this solution. After alot of debugging with support over many days there is an undocumented bug with this. The integration works perfectly for small payloads. But if your message is too long SNS will respond with a 400 Bad Request and no error message. This is because the message caused the URL to become too large.

To fix this change the integration to as below.

"requestParameters": {
       "integration.request.header.Content-Type": "'application/x-www-form-urlencoded'"
},
"requestTemplates" : {
    "application/json" : {
              "Fn::Sub": "Action=Publish&TopicArn=$util.urlEncode('${**<TOPIC-ARN>**}')&Message=$util.urlEncode($input.body)##"
    }
}
replied 5 months ago

Thank you for your comment. We'll review and update the Knowledge Center article as needed.

profile pictureAWS
MODERATOR
replied 5 months ago