serverless/serverless

HTTP API Request/Response Transform Parameter Mapping

Open

#9,803 opened on 2021年8月3日

GitHub で見る
 (8 comments) (2 reactions) (0 assignees)JavaScript (46,915 stars) (5,734 forks)batch import
cat/aws-event-http-apienhancementhelp wantedneeds feedback

説明

Use case description

API Gateway v2 HTTP API supports transforming requests and responses (also referred to as parameter mapping). I would like Serverless to provide an easy way to configure this feature. Ideally it could be set on the provider level to apply to all functions and overridden on individual functions.

Transforming API requests and responses AWS::ApiGatewayV2::Integration

Proposed solution

Rough draft. Partial configuration, using JS syntax:

const serverlessConfig = {
    provider: {
        httpApi: {
            requestParameterMapping: {
                "append:header.experiment": "$request.path"
            },
            responseParameterMapping: {
                200: {
                    "append:header.cache-control": "no-cache"
                },
                204: {
                    "append:header.cache-control": "no-cache"
                }
            }
        }
    },
    functions: {
        example: {
            httpApi: {
                requestParameterMapping: {
                    "append:header.experiment": "$request.path"
                },
                responseParameterMapping: {
                    200: {
                        "append:header.cache-control": "no-cache"
                    }
                }
            }
        }
    }
}

requestParameterMapping and responseParameterMapping could be named requestParameters and responseParameters to correspond to the CloudFormation, but I thought the word "mapping" was more explicit and easier to associate with the HTTP API documentation.

I represented both as maps rather than arrays. CloudFormation actually uses a map for RequestParameters whereas ResponseParameters is an array of objects with separate Source and Destination properties. Nonetheless, if you specify two ResponseParameters with the same Destination, only the second one takes affect. For example, you cannot append two values to the same header; although CloudFormation allows it syntactically, I have determined experimentally that the first mapping is overridden by the second. Representing responseParameterMapping[status] as a map in Serverless would avoid unexpected results for users if they tried to specify two mappings for the same destination, since it is not syntactically possible this way. However it could prevent Serverless from supporting this capability if AWS were to change the service someday and start supporting multiple mappings for the same destination.

This example shows how you could specify the mapping on the provider to apply to all functions or set/override it on a single function. If you configure it on both the provider and the function, I'm not sure whether it would be better to merge or completely override the mapping from the provider with that on the function. With merging, if you wanted to remove an inherited mapping key, you would have to set it to null on the function. On the other hand you could make the entire map on the function override the provider, in which case users would have to repeat any entries from the provider they wish to include.

On the function level, the configuration goes directly in httpApi, not events[n].httpApi, because it is associated with the HTTP API Integration, not the Route.

Response mapping is per HTTP status. It would be nice to have a way to specify the same mapping for multiple statuses. I've shown an example below. The downside to arrays is it becomes possible to specify the same status code more than once and those maps would have to be merged when converting to CloudFormation, and an error raised for duplicate keys.

// Alternative syntax fragment for specifying same mappings for multiple status codes
responseParameterMapping: [
    {
        httpStatus: [ 200, 204 ],
        "append:header.cache-control": "no-cache"
    }
]

CloudFormation:

    "HttpApiIntegrationExample": {
      "Type": "AWS::ApiGatewayV2::Integration",
      "Properties": {
        "ApiId": {
          "Ref": "HttpApi"
        },
        "IntegrationType": "AWS_PROXY",
        "IntegrationUri": {
          "Fn::GetAtt": [
            "ExampleLambdaFunction",
            "Arn"
          ]
        },
        "PayloadFormatVersion": "2.0",
        "TimeoutInMillis": 29000,
        "RequestParameters": {
          "append:header.experiment": "$request.path"
        },
        "ResponseParameters": {
          "200": {
            "ResponseParameters": [
              {
                "Destination": "append:header.cache-control",
                "Source": "no-cache"
              }
            ]
          },
          "204": {
            "ResponseParameters": [
              {
                "Destination": "append:header.cache-control",
                "Source": "no-cache"
              }
            ]
          }
        }
      }
    }

Workaround

Until this feature is implemented I work around it using resources.extensions:

  resources: {
    extensions: {
      HttpApiIntegrationMain: {
        Properties: {
          ResponseParameters: {
            200: {
              ResponseParameters: [
                {
                  Destination: "append:header.cache-control",
                  Source: "no-cache"
                }
              ]
            },
//...

コントリビューターガイド