2

I am using Pulumi to create an AWS API Gateway that proxies to a Lambda backend. The API is used by a web frontend, and my Lambda Function handles CORS in most situations. However, if the Usage Plan for my API key is exceeded, the Gateway sends a 429 without any CORS headers, so the web frontend can't accurately determine the problem. What I would like is for the page to be able to display a dedicated "Server is overloaded" error when the Usage Plan quota is exceeded instead of just a generic "something went wrong."

I realize that I can enable CORS on the Gateway, but it looks like I can only do that manually via the console, and I want to use Pulumi exclusively.

I see that this answer says to use a Gateway Response, and it links to this answer that additionally emphasizes to "Make sure to re-deploy your gateway."

I created Gateway Responses that included the headers like this:

    addGatewayResponse(restApi.api.id, "429", "QUOTA_EXCEEDED");
    addGatewayResponse(restApi.api.id, "429", "THROTTLED");
    addGatewayResponse(restApi.api.id, "403", "INVALID_API_KEY");
    addGatewayResponse(restApi.api.id, "403", "MISSING_AUTHENTICATION_TOKEN");
    addGatewayResponse(restApi.api.id, "404", "RESOURCE_NOT_FOUND");
    addGatewayResponse(restApi.api.id, "400", "DEFAULT_4XX");
    addGatewayResponse(restApi.api.id, "500", "DEFAULT_5XX");

    function addGatewayResponse(
      restApiId: pulumi.Output<string>,
      statusCode: string,
      responseType: string
    ) {
      new aws.apigateway.Response(`${name}-GatewayResponse-${responseType}`, {
        restApiId: api.api.id,
        responseType,
        statusCode,
        responseTemplates: {
          "application/json": JSON.stringify({
            message: "$context.error.messageString",
            type: "$context.error.responseType",
            statusCode,
            resourcePath: "$context.resourcePath",
          }),
        },
        responseParameters: {
          "gatewayresponse.header.Access-Control-Allow-Origin": "'*'",
          "gatewayresponse.header.Access-Control-Allow-Headers": "'*'",
          "gatewayresponse.header.Access-Control-Allow-Methods": "'*'",
        },
      });
    }

and I completely destroyed and recreated my stack. I can log into the console and see that my Access-Control-Allow-* headers and the template/parameters are configured on the Gateway Response for all the response types the way I expect.

However, when I try to test this using curl (by either omitting the api key, or manually setting the quota to a low number) I just get a standard response with no CORS headers:

< HTTP/2 429
< date: Thu, 27 Oct 2022 22:30:31 GMT
< content-type: application/json
< content-length: 28
< x-amzn-requestid: xxx
< x-amzn-errortype: LimitExceededException
< x-amz-apigw-id: xxx
< 
* Connection #0 to host xxx.execute-api.xxx.amazonaws.com left intact
{"message":"Limit Exceeded"}

When I test via the web app, fetch throws an error (TypeError: NetworkError when attempting to fetch resource.) and my JavaScript does not have access to the response status code (as far as I can tell).

I am hitting the stage's invoke URL directly (bypassing any domain) and I tried making the gateway regional instead of edge optimized to (I think) avoid having Cloudfront in the middle.

If I provide the API key and set a normal quota, everything works fine.

So it seems like my Gateway Response configuration is just...not being used at all?

What am I missing?

Dave
  • 967
  • 7
  • 13

0 Answers0