I'm building an app that connects with a serverless API on AWS (API Gateway/Lambda) via AWS Amplify used on a web (React) frontend.
When the request is successful, everything works just fine (definitionally, I suppose). There are no CORS issues or anything. But if I try to return a "failed" response, my frontend does not receive that response. It receives a generic Network Error
with no information about what happened, and the console logs a failure.
I think the code will make more sense of this. I request to the API using code like this (Amplify configured elsewhere):
import { API } from 'aws-amplify';
...
API.get('apiName', path, options)
.then(response => {
// Whatever
})
.catch(err => {
// Whatever
})
In the Lambda, I'm returning (and Cloudwatch verifies this, as I log right before running callback(null, responseObject)
) a full response object like this:
{
"statusCode": 200,
"headers": {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true
},
"body": "{ /* whatever */ }"
}
or this:
{
"statusCode": 500,
"headers": {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true
},
"body": "{ /* error here */ }"
}
In the 200
case, the response reaches my frontend perfectly (shows up in the .then
function as response
). In the 500
(or general error) case, the .catch
is called and the err
object is not what I put in the response body, but rather a generic Network Error
with no relevant information (or status code):
Error: Network Error
at createError (createError.js:16)
at XMLHttpRequest.handleError (xhr.js:87)
To clarify, simply changing the statusCode
from 200 to 400 both means the request is caught on the frontend (good), and that the error has none of the information I'm returning from the Lambda (bad).
The console also logs errors:
DELETE https://abcdefg.execute-api.us-west-2.amazonaws.com/development/my-endpoint 403 ()
Failed to load https://abcdefg.execute-api.us-west-2.amazonaws.com/development/my-endpoint: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. The response had HTTP status code 403.
It also logs stuff related to a CORS failure that makes no sense to me, because I'm following the exact same pattern as for success cases, just with a higher statusCode
.
Cross-Origin Read Blocking (CORB) blocked cross-origin response https://abcdefg.execute-api.us-west-2.amazonaws.com/development/my-endpoint with MIME type application/json.
XHR failed loading: DELETE "https://abcdefg.execute-api.us-west-2.amazonaws.com/development/my-endpoint".
Any idea what's going on here? It's really frustrating not to have access to the real informative error on the frontend. Please let me know if there's any other information that would be helpful for solving this issue.
EDIT -- following @Michael's feedback below, I checked to see if my app was using lambda or lambda-proxy, and it is, in fact, using lambda-proxy, which should not produce this behavior, as I understand it.
Here's some code in my serverless.yml file,
functions:
projectsGetAll:
handler: handlers/projects/getAll.handler
events:
- http:
path: projects
method: get
cors: true
authorizer: aws_iam
And here's a screenshot (with the endpoint whited out) from AWS API Gateway for one of these endpoints:
I want to confirm that this is, then, unexpected behavior for lambda-proxy. Is there possibly some setting that is mucking things up?
Figured it out! The error object coming back (a generic 500
and NetworkError
contained my constructed error, under the response
key).
So if I log error.response
, I get my fully constructed error object, with its own message and statusCode
, even though the error object itself has a code
of 500
and message of Error: Network Error
, and the error catching process seems to throw out inaccurate error logs (CORB stuff) in the console.
Weird. Not sure if that's expected Lambda behavior, or something with Amplify, or something I'm doing wrong, but if anyone else runs into this issue, I hope this helps!
EDIT - Apparently that is expected Amplify behavior, which I missed in the docs.
But I figured out another issue that seems to be a bug. This response key only shows up when I don't specify headers
in the request options. In other words, this request returns an error
with a response
key:
API.get('my-api', '/path')
But this one returns an error without the key:
API.get('my-api', '/path', { headers: { 'Content-Type': 'application/json' } })
Very weird, but true for me. Even other options are fine, but the headers option seems to mess things up (and only for the error response).
This does end up depending on how you configure your API Gateway, e.g. Lambda proxy etc. If you are NOT using a Lambda proxy setup, you will need to map these error responses/codes within API Gateway, see extensive post on that here: https://aws.amazon.com/blogs/compute/error-handling-patterns-in-amazon-api-gateway-and-aws-lambda/
If you are using a Lambda Proxy, you will just return as a specific type of proxy response from Lambda: https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With