26

I'm very new to the API Gateway and Lambda. I'm attempting to deploy a Node.js Express-based API to lambda. I'm using the aws-serverless-express example from awslabs. As such, much of my AWS configuration was automatically created for me.

It seems like my API is working correctly via the API Gateway. My post and get methods work fine. However, I need to support CORS. My application should be returning the correct CORS response to OPTIONS requests, but it's not working on AWS.

Ultimately, no matter what I do, I receive a 500 response to options requests. I haven't been able to figure out how to get any information about these 500 errors. I'm not sure what's causing them.

This is the body of the 500 response {"message": "Internal server error"}.

These are the response headers:

content-length:36

content-type:application/json

date:Sun, 09 Jul 2017 17:56:24 GMT

status:500

via:1.1 9af17e5a616bfc9ac07fc7e415ade9e6.cloudfront.net (CloudFront)

x-amz-cf-id:1_AZmkLqf1rjkog2MRtvcBAe54aIZdPWmNApBTwG48Af-v_g9WHkZw==

x-amzn-requestid:ec216a62-64cf-11e7-ad2b-4f1e96508dba

x-cache:Error from cloudfront

I'm pretty sure my OPTIONS request isn't even getting to the app on Lambda.

I've tried configuring CORS using the API Gateway (and in my app). I'm trying to configure it to allow all origins.

If there anything I could be looking at or doing to debug this problem?

Edit:

In an attempt to debug this issue I tried to enable logging in CloudWatch for the API gateway.

CloudWatch configuration for the API Gateway

After doing that, I see these two gateway-lookin' logs in CloudWatch:

enter image description here

I've been using prod so I click on that link and see this:

enter image description here

I assume this is a long list of log entries. I'm not sure what "streams" means in this context. There are hundreds of these entries. So, I pick the one with the most recent timestamp and click on it. Now I see this:

enter image description here

It seems that all of my gateway logs look like this. IE: apparently empty.

So, am I setting up logging correctly? Am I looking in the right place?

5
  • What's in the body of the 500 error? Commented Jul 9, 2017 at 15:40
  • 1
    The only content in the body is: {"message": "Internal server error"} Commented Jul 9, 2017 at 17:57
  • I've updated the question with a few more details I've pulled from Chrome. Commented Jul 9, 2017 at 17:59
  • The exact error "Internal server error" will be logged in cloudwatch logs that you can enable on a stage. Note, "Internal server error" will be returned if there is any configuration error on the API. This is the behavior since the API consumers will be your users who shouldn't really see any debugging information about your API. Commented Jul 10, 2017 at 5:31
  • I'm not clear on how to use CloudWatch with the API Gateway. I've added an ARN under "CloudWatch log role ARN" in settings. However, I can't make sense of what shows up in CloudWatch when I do this. I'm not even sure I'm looking at the right thing. I'll add more details above regarding the logging. Commented Jul 10, 2017 at 16:14

8 Answers 8

8

I have similar issue as @scopchanov .

What works in my AWS Lambda + API Gateway + Serverless Framework 1.42.3 + Express.js project is:

  1. I have to enable CORS / send headers from Express:

    // AWS API Gateway CORS (OPTIONS) support is buggy? app.use(cors({credentials: true})); app.options("*", cors());

  2. Do not enable CORS in API Gateway, i.e. don't do this in serverless.yml :

    • http: method: ANY path: '{proxy+}' # will cause {"message": "Internal server error"} # cors: true

Without cors: true, all requests will work fine.

With cors: true, OPTIONS requests will respond with {"message": "Internal server error"} (regardless of what the Lambda function does, it seems that this is sent directly by API Gateway, or a buggy MOCK integration).

Sign up to request clarification or add additional context in comments.

1 Comment

I set a same options like yours, but It occured occasionally "Internal server error". I don't know how to do... 😇
6

For those who still face OPTIONS 500 error with aws-serverless-express the solution is to add contentHandling: CONVERT_TO_TEXT to OPTIONS method in the Swagger template

Here is the swagger template file that contains / and /{proxy+} paths

---
swagger: 2.0
info:
  title: AwsServerlessExpressApi
basePath: /prod
schemes:
- https
paths:
  /:
    x-amazon-apigateway-any-method:
      produces:
      - application/json
      responses:
        200:
          description: 200 response
          schema:
            $ref: "#/definitions/Empty"
      x-amazon-apigateway-integration:
        responses:
          default:
            statusCode: 200
        uri: arn:aws:apigateway:YOUR_AWS_REGION:lambda:path/2015-03-31/functions/arn:aws:lambda:YOUR_AWS_REGION:YOUR_ACCOUNT_ID:function:${stageVariables.ServerlessExpressLambdaFunctionName}/invocations
        passthroughBehavior: when_no_match
        httpMethod: POST
        type: aws_proxy
    options:
      consumes:
      - application/json
      produces:
      - application/json
      responses:
        200:
          description: 200 response
          schema:
            $ref: "#/definitions/Empty"
          headers:
            Access-Control-Allow-Origin:
              type: string
            Access-Control-Allow-Methods:
              type: string
            Access-Control-Allow-Headers:
              type: string
      x-amazon-apigateway-integration:
        contentHandling: CONVERT_TO_TEXT
        responses:
          default:
            statusCode: 200
            responseParameters:
              method.response.header.Access-Control-Allow-Methods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'"
              method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
              method.response.header.Access-Control-Allow-Origin: "'https://example.com'"
        passthroughBehavior: when_no_match
        requestTemplates:
          application/json: "{\"statusCode\": 200}"
        type: mock
  /{proxy+}:
    x-amazon-apigateway-any-method:
      produces:
      - application/json
      parameters:
      - name: proxy
        in: path
        required: true
        type: string
      responses: {}
      x-amazon-apigateway-integration:
        uri: arn:aws:apigateway:YOUR_AWS_REGION:lambda:path/2015-03-31/functions/arn:aws:lambda:YOUR_AWS_REGION:YOUR_ACCOUNT_ID:function:${stageVariables.ServerlessExpressLambdaFunctionName}/invocations
        httpMethod: POST
        type: aws_proxy
    options:
      consumes:
      - application/json
      produces:
      - application/json
      responses:
        200:
          description: 200 response
          schema:
            $ref: "#/definitions/Empty"
          headers:
            Access-Control-Allow-Origin:
              type: string
            Access-Control-Allow-Methods:
              type: string
            Access-Control-Allow-Headers:
              type: string
      x-amazon-apigateway-integration:
        contentHandling: CONVERT_TO_TEXT
        responses:
          default:
            statusCode: 200
            responseParameters:
              method.response.header.Access-Control-Allow-Methods: "'DELETE,GET,HEAD,OPTIONS,PATCH,POST,PUT'"
              method.response.header.Access-Control-Allow-Headers: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
              method.response.header.Access-Control-Allow-Origin: "'https://example.com'"
        passthroughBehavior: when_no_match
        requestTemplates:
          application/json: "{\"statusCode\": 200}"
        type: mock
x-amazon-apigateway-binary-media-types:
  - '*/*'
definitions:
  Empty:
    type: object
    title: Empty Schema

More info can be found on GitHub Internal server error when request method OPTIONS

Hope this helps!

Comments

5

I'm deploying with a SAM template and I've been stuck with this error for days. Finally I found that I had to clear everything in the "Binary Media Types" in the "Settings" tab of the API Gateway.

enter image description here

2 Comments

Thanks so much, this is the only thing that worked for me. For anyone doing this in Pulumi rather than SAM, I used type: aws-apigateway:RestAPI and binaryMediaTypes: - ""
Why did this work?
3

I had the same problem when creating a serverless solution with cloudformation. Cloudformation creates API Gateway + Lambda and everything looks okay. However OPTIONS requests were returning internal server error.

I had to go to API Gateway and manually publish my API only once. (Eventhough it was already published to prod stage) Then OPTIONS requests worked. I can now use cloudformation to update.

Comments

3

Was having really tough time finding any clue about this 500 error from API gateway which started coming after I enabled CORS on the proxy resource. I was getting bellow 500 - InternalServerErrorException for the OPTIONS method call. enter image description here

Peter's response (2nd Answer) actually resolved my issue as I was also using sam template for deployment. By deleting the "Binary Media Type" entries from the API Settings / changing to

BinaryMediaTypes:
      - ""

in the sam template file, I got the 500 error removed. Thanks a ton Peter!, for the very helpful answer. :-)

enter image description here

Comments

2

I just went through this...hidden in the AWS lambda docs on enabling CORS was the fact that you have to set the CORS header in your lambda. So here is how to do that:

let payload = {
    statusCode: 400,
    body: JSON.stringify('body'),
    headers: {"Access-Control-Allow-Origin": "*"} // NEED this for API CORS access
};
callback(null, payload);

You must return a valid statusCode and body along with the headers, or the API will fail to transform your lambda response to an API response.

3 Comments

Unfortunately, I've got the (I think) equal in JavaScript already: app.use( cors({ origin: '*' }) );
I've done this manually too, not using the cors library and, even if I manually implement an OPTIONS endpoint I don't see anything in the lambda logs.
Not the same. This is javascript, ignore the IRepsonsePayload, that's just the TypeScript typing. You have to pass the header back with: headers: {"Access-Control-Allow-Origin": "*"}
1

For me, the solution was to change:

cors: true

to

cors:
  origin: '*'

I'm not sure what's the difference, but one gives me Internal server error and the other does not.

1 Comment

Worked for me, thoug just removing cors flag on yml works too. the point being was setting the cors via express .use(..)
-2

You can use following code in your routes index file

app.options(function(req,res){res.send(200);})

Here app is the router object of express.This will send all the options request to 200.

1 Comment

I removed my use of the cors package and added this to no effect. I can show that get/post requests make it to my API, but I don't think options requests are at all: // make all options requests return 200 app.options('', (req, res) => { console.log('FSsdfafsadfdsa'); res.append('Access-Control-Allow-Origin', ''); res.sendStatus(200); });

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.