HTTP Proxy via CloudFormation

Hello folks, I’m using the example provided in the docs to deploy a pass-through HTTP proxy alongside my lambda function, and I’m afraid it’s missing a few important settings that are easy to enable in the API Gateway UI. Specifically, the Use HTTP Proxy Integration setting on the Integration Request page adds settings like the URL Path Parameter proxy mapping to method.request.path.proxy. I can’t tell how it allows querystrings to pass through though.

Does anyone know how to replicate this functionality via CloudFormation template, so I can deploy it alongside my serverless lambda function?

Goal: myserverlesssite.com/carto/api/v2/sql?q=select ... is proxied to phl.carto.com/api/v2/sql?q=select ...

Here’s how I’ve been approaching it:

resources:
  Resources:
    ProxyResource:
      Type: AWS::ApiGateway::Resource
      Properties:
        ParentId:
          Fn::GetAtt:
            - ApiGatewayRestApi # our default Rest API logical ID
            - RootResourceId
        PathPart: '{proxy+}' # the endpoint in your API that is set as proxy
        RestApiId:
          Ref: ApiGatewayRestApi
    ProxyMethod:
      Type: AWS::ApiGateway::Method
      Properties:
        ResourceId:
          Ref: ProxyResource
        RestApiId:
          Ref: ApiGatewayRestApi
        AuthorizationType: NONE
        HttpMethod: GET # the method of your proxy. Is it GET or POST or ... ?
        MethodResponses:
          - StatusCode: 200
        Integration:
          IntegrationHttpMethod: GET
          Type: HTTP
          Uri: https://phl.carto.com/{proxy} # the URL you want to set a proxy to
          IntegrationResponses:
            - StatusCode: 200
        RequestParameters:
          integration.request.path.proxy: proxy

I came across this blog post which pointed out that you can include swagger in CloudFormation. But it appears only for ApiGateway cloudformation. So I gave it a shot, exporting the swagger of the working api I configured manually, then pulled out just the http proxy part, and gave it a go in serverless.yml.

It deploys properly, but ends up overwriting the other serverless-created resources :-/

resources:
  Resources:
    ApiGatewayRestApi: # default serverless logical id
      Type: AWS::ApiGateway::RestApi
      Properties:
        Name: dev-soda-carto
        Body:
          swagger: '2.0'
          schemes:
            - https
          paths:
            /carto/{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: "https://phl.carto.com/{proxy}"
                  requestParameters:
                    integration.request.header.Accept-Encoding: "'identity'"
                    integration.request.path.proxy: "method.request.path.proxy"
                  passthroughBehavior: "when_no_match"
                  httpMethod: "ANY"
                  cacheNamespace: "jf083p"
                  cacheKeyParameters:
                  - "method.request.path.proxy"
                  type: "http_proxy"

Update: I got it working! I noticed that the exported swagger is basically cloudformation, so that told me what pieces were missing from the original cloudformation.

resources:
  Resources:
    ProxyParentResource:
      Type: AWS::ApiGateway::Resource
      Properties:
        ParentId:
          Fn::GetAtt:
            - ApiGatewayRestApi # serverless default Rest API logical ID
            - RootResourceId
        PathPart: 'carto'
        RestApiId:
          Ref: ApiGatewayRestApi
    ProxyResource:
      Type: AWS::ApiGateway::Resource
      Properties:
        ParentId:
          Ref: ProxyParentResource
        PathPart: '{proxy+}' # the endpoint in your API that is set as proxy
        RestApiId:
          Ref: ApiGatewayRestApi
    ProxyMethod:
      Type: AWS::ApiGateway::Method
      Properties:
        ResourceId:
          Ref: ProxyResource
        RestApiId:
          Ref: ApiGatewayRestApi
        AuthorizationType: NONE
        HttpMethod: GET # the method of your proxy. Is it GET or POST or ... ?
        MethodResponses:
          - StatusCode: 200
        RequestParameters:
          method.request.path.proxy: true
        Integration:
          IntegrationHttpMethod: GET
          Type: HTTP_PROXY
          Uri: https://phl.carto.com/{proxy} # the URL you want to set a proxy to
          IntegrationResponses:
            - StatusCode: 200
          RequestParameters:
            integration.request.path.proxy: method.request.path.proxy
            integration.request.header.Accept-Encoding: "'identity'"
          PassthroughBehavior: WHEN_NO_MATCH
6 Likes

The AWS documentation is poor in many places but I’ve found it almost unusable when it comes to API Gateway, so thanks for the above!
I’d got almost all the way to that by inspecting the integration PATCH requests when manually setting them up in the management console, but in the process removed the RequestParameters from the method.

1 Like