I am using AWS CDK (v1.87.1 (build 9eeaa93)) to define my infrastructure as code. I use C# to define my CDK stack(s).
I have my data stored in DynamoDb and an API gateway backed by Lambda functions to read/write to the DynamoDb. This is my backend.
My frontend is a simple static website (HTML + JS) hosted on AWS S3 distributed through CloudFront.
My API works fine when I test it independently with curl or in the AWS console. However, when I call the API using the fetch() browser API from within my static website page, I get the following error (in the browser):
Access to fetch at 'https://xxxxxxxx.execute-api.ap-south-1.amazonaws.com/prod/Account' from origin 'https://abcdefg.cloudfront.net' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
My CorsOptions are defined as follows:
var defaultCorsPreflightOptions = new CorsOptions() {
AllowOrigins = Cors.ALL_ORIGINS,
AllowMethods = Cors.ALL_METHODS,
AllowHeaders = new [] {"*"},
AllowCredentials = true,
MaxAge = Duration.Days(0)
};
My API is as follows:
var api = new RestApi(this, "my-api", new RestApiProps {
RestApiName = "My Service",
Description = "This is the service API"
});
My resource creation adds the CorsOption for preflight (In the above error message 'Account' would be a resource added to the root):
var resourceType = api.Root.AddResource(ent);
resourceType.AddCorsPreflight(defaultCorsPreflightOptions);
My lambda handler also has
if(method == "OPTIONS") {
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers" : "Content-Type",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "OPTIONS,POST,PUT,GET,DELETE"
}
};
return response;
} else if(method === "POST") {
// ... omitted
}
The JS client code that calls the REST API is:
const response = await fetch(`${api_url}/${entity}`,{
method: 'POST',
mode: 'cors',
body: item,
headers: {
'Content-Type': 'application/json'
}
});
The Behavior that is attached to the CloudFront distribution:
// The cloudfront distribution for the website
var behavior = new Behavior() {
IsDefaultBehavior = true,
AllowedMethods = CloudFrontAllowedMethods.ALL,
MaxTtl = Duration.Seconds(0),
MinTtl = Duration.Seconds(0),
DefaultTtl = Duration.Seconds(0),
Compress = false,
ForwardedValues = new CfnDistribution.ForwardedValuesProperty() {
QueryString = true,
Headers = new [] {"Authorization", "Access-Control-Allow-Origin"}
}
};
My CloudFront distribution is as follows:
var distribution = new CloudFrontWebDistribution(this, "StaticWebsiteDistribution", new CloudFrontWebDistributionProps() {
OriginConfigs = new [] {
new SourceConfiguration() {
S3OriginSource = new S3OriginConfig() {
S3BucketSource = bucket
},
Behaviors = new [] {
behavior
}
}
}
});
My S3 Bucket deployment code is:
// The S3 bucket deployment for the website
var deployment = new BucketDeployment(this, "WebsiteDeployment", new BucketDeploymentProps(){
Sources = new [] {Source.Asset("./website")},
DestinationBucket = bucket,
Distribution = distribution
});
I have tried looking into the AWS CDK documentation. I have tried adding the default CORS option at the API level also, but without any success. What am I missing? Please help.
I figured it out. The error was was in the Lambda handler for the POST / GET / DELETE / PUT methods. I needed to return the headers (example given below):
return {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*", // I was missing this
"Content-Type": "application/json" // and this
},
body: JSON.stringify({"id":`${id}`}) // and this was a string earlier
};
I had another error in the client side with the fetch() response handling. I was using response.json() whereas it should have been response.text() (since I was sending text in the response body earlier).
I was misled by the curl response (in my testing) which was just the plain text whereas there was a JSON parse issue with handling the fetch() response.
Key takeaway: Check your Lambda handler responses.