Handle API Gateway CORS Errors

Link to chapter - http://serverless-stack.com/chapters/handle-api-gateway-cors-errors.html

Hi guys,

It seems like this chapter is accidentally being skipped in your tutorial.

Clicking ā€˜nextā€™ from What is Infrastructure links to Configure DynamoDB. The DynamoDB chapterā€™s serverless.yml Resources section references resources/api-gateway-errors.yml. I think What is Infrastructure should link this chapter instead, and this chapterā€™s ā€˜nextā€™ should link to DynamoDB.

By the way, this tutorial is excellent, thank you.

Oh this chapter is a new addition to Part 1 of the tutorial. Did you by any change complete Part 1 some time ago?

Oh, interesting, I see what you mean. When I worked through the tutorial, it had me going into API gateway and manually setting this up. I have been focused on just the infra as code section recently, which assumes resources/api-gateway-errors.yml is already hooked up correctly.

Thanks, nevermind!

1 Like

Hi,

So far the tutorial has been wonderful. Iā€™m now splitting up my apis into separate services following your tutorial as it hit the 200 limit error yesterday! (something similar to serverless-stack-demo-mono-api but the db and auth will be separate repos). Wondering where to include the API Gateway errors resource in this architecture? Couldnt find anything related to this in your github sample.

Any clue?

Thanks!
Mohsin

Thanks!

Sorry which API Gateway errors resource are you talking about? Is it this one - https://github.com/AnomalyInnovations/serverless-stack-demo-api/blob/master/resources/api-gateway-errors.yml?

If that is the case then you should be able to reference it just as before.

Hi Jay,

Thanks for your reply. Yes thatā€™s the resource I was referring to. If there are more than one services, should each service refer this api gateway errors resource? I guess it should! Just wanted to confirm.

Thanks!

So this isnā€™t super obvious but this errors resource is tied to a API Gateway project. In the case of the serverless-stack-demo-mono-api, we add all our APIs to the same project. So we only need to have this resource once. We talk about using the same project at the bottom of this chapter:

Have you ever had some requests working and some failing with CORS issues targeting the same backend? Iā€™m having this weird thing happening:

  • my requests go through except for one with query string parameters
  • this one request works in chrome but not firefox
  • in firefox, I get a vague network error and the browser logs that cors failed (Reason: CORS request did not succeed)

Iā€™ve asked my question there but didnā€™t get much luck: https://forums.aws.amazon.com/thread.jspa?messageID=891922&tstart=0#891922

Iā€™ve not had that specific issues but we do have a few requests fail every once in a while.

From reading your issue it seems like you are able to replicate it consistently. Can you put your code up in a repo?

Hi everyone,

Iā€™m trying to deploy but itā€™s not going through! The error has so many lines that I cant reach the beginning of the statement so I canā€™t really read much.

Everything before this works like a charm!

Iā€™m pretty sure it has to be with either the serverless.yml or the api-gateway-errors.yml - indentation specifically!

Hereā€™s my serverless.yml

# NOTE: update this with your service name
service: d2as-hiring-api

# Create an optimized package for our functions 
package:
  individually: true

plugins:
  - serverless-bundle # Package our functions with Webpack
  - serverless-offline

provider:
  name: aws
  runtime: nodejs10.x
  stage: prod
  region: us-east-1

  # 'iamRoleStatements' defines the permission policy for the Lambda function.
  # In this case Lambda functions are granted with permissions to access DynamoDB.
  iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:DescribeTable
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: "arn:aws:dynamodb:us-east-1:*:*"

functions:
  # Defines an HTTP API endpoint that calls the main function in create.js
  # - path: url path is /interviews
  # - method: POST request
  # - cors: enabled CORS (Cross-Origin Resource Sharing) for browser cross
  #     domain api call
  # - authorizer: authenticate using the AWS IAM role
  create:
    handler: create.main
    events:
      - http:
          path: interviews
          method: post
          cors: true
          authorizer: aws_iam

  get:
    # Defines an HTTP API endpoint that calls the main function in get.js
    # - path: url path is /interviews/{id}
    # - method: GET request
    handler: get.main
    events:
      - http:
          path: interviews/{id}
          method: get
          cors: true
          authorizer: aws_iam

  list:
    # Defines an HTTP API endpoint that calls the main function in list.js
    # - path: url path is /interviews
    # - method: GET request
    handler: list.main
    events:
      - http:
          path: interviews
          method: get
          cors: true
          authorizer: aws_iam
  
  update:
    # Defines an HTTP API endpoint that calls the main function in update.js
    # - path: url path is /notes/{id}
    # - method: PUT request
    handler: update.main
    events:
      - http:
          path: interviews/{id}
          method: put
          cors: true
          authorizer: aws_iam
  
  delete:
    # Defines an HTTP API endpoint that calls the main function in delete.js
    # - path: url path is /notes/{id}
    # - method: DELETE request
    handler: delete.main
    events:
      - http:
          path: interviews/{id}
          method: delete
          cors: true
          authorizer: aws_iam
# Create our resources with separate CloudFormation templates

resources:
  # API Gateway Errors
  - ${file(resources/api-gateway-errors.yml)}

As you can see,the resources indentation matches the functions, provider, service level.

Hereā€™s api-gateway-errors.yml

Resources:
  GatewayResponseDefault4XX:
    Type: 'AWS::ApiGateway::GatewayResponse'
    Properties:
      ResponseParameters:
         gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
         gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
      ResponseType: DEFAULT_4XX
      RestApiId:
        Ref: 'ApiGatewayRestApi'
  GatewayResponseDefault5XX:
    Type: 'AWS::ApiGateway::GatewayResponse'
    Properties:
      ResponseParameters:
         gatewayresponse.header.Access-Control-Allow-Origin: "'*'"
         gatewayresponse.header.Access-Control-Allow-Headers: "'*'"
      ResponseType: DEFAULT_5XX
      RestApiId:
        Ref: 'ApiGatewayRestApi'

Hmmm itā€™s going to be really hard to debug this without the error message though. Can you save the output of the error to a file and then look up what the error message was?

Thanks for your hint on adding the api gateway errors resource to just the root-api when you are splitting the services. Prior to making that change, I was getting CORS errors when attempting to call the API from my front end app. After implementing the change and adding the resource to only the root-api, the CORS error went away but then I got a 403 Missing Authentication Token. Do I need to set the authorizer in the root-api handler route to authorizer: aws_iam ? Or something else?

Hmmm so the authorizer needs to be applied to every single endpoint and Lambda function you have.

However, this error can be very misleading at times. Sometimes youā€™ll get this error if you are hitting an endpoint that you are not handling.

Hi all.
I canā€™t upload a file, but a single note if

This looks like a CORS error, have you configured your IAM roles to allow your users access to the S3 bucket?

When I try to use the test-api I get a http 500 error. I tried running serverless invoke --function create --path mocks/create-event.json and got the same 500 error there. When I run the test api utility I get the following

Making API request

{
   status: 403,
   statusText: 'Forbidden',
   data: '{"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.\n' +
   '\n' +
   'The Canonical String for this request should have been\n' +
   "'POST\n" +
   '/prod/notes\n' +
   '\n' +
   'accept:application/json\n' +
   'content-type:application/json\n' +
   'host:unbtq4ium0.execute-api.eu-west-2.amazonaws.com\n' +
   'x-amz-date:20191227T175028Z\n' +
   '\n' +
   'accept;content-type;host;x-amz-date\n' +
   "3a99f7c41ea871222ce9eb05cc8c7a5bbfc8e141bbb3c3999cff381d1462d448'\n" +
   '\n' +
   'The String-to-Sign should have been\n' +
   "'AWS4-HMAC-SHA256\n" +
   '20191227T175028Z\n' +
   '20191227/eu-west-2/execute-api/aws4_request\n' +
  "614c1776e4a9a523adf669a111e77ecfe9a486c9fa83fa3e59a56a2e5d956620'\n" +
  '"}'
}

Any advice will be much appreciated

Hmm the error from the test utility is a little different from the other 500 errors Iā€™m guessing.

For the 500 errors, they are generated in our code. So you can add a console.log before returning an error and check the logs.