I have a React frontend that lets users add and view recipes, which contain text and a picture. The backend is an Express Node.JS app which reads from and writes to a DynamoDB database. The application is deployed on AWS using the Serverless Framework, so it uses API Gateway, Lambda, DynamoDB, and S3 for photo storage. I'm working on getting the uploadphoto route working, but CORS errors are preventing it from working.
I've imported the cors NPM module and am using it on the app. I've tried explicity specifying the origin in the config, but that doesn't make a difference. I also have cors: true on every route in my serverless.yml file.
serverless.yml excerpt:
service: recipes-api
provider:
name: aws
runtime: nodejs8.10
stage: dev
region: us-east-1
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource:
- { "Fn::GetAtt": ["RecipesDynamoDBTable", "Arn"] }
environment:
RECIPES_TABLE: ${self:custom.tableName}
S3_BUCKET: ${self:custom.bucketName}
functions:
app:
handler: index.handler
events:
- http: ANY /
- http: 'ANY {proxy+}'
getRecipe:
handler: index.handler
events:
- http:
path: recipes/{name}
method: get
cors: true
allRecipes:
handler: index.handler
events:
- http:
path: allrecipes
method: get
cors: true
addRecipe:
handler: index.handler
events:
- http:
path: recipes
method: post
cors: true
uploadPhoto:
handler: index.handler
events:
- http:
path: uploadphoto
method: post
cors: true
getPhoto:
handler: index.handler
events:
- http:
path: photo/{name}
method: get
cors: true
index.js excerpt:
const serverless = require('serverless-http');
const express = require('express');
const app = express();
const AWS = require('aws-sdk');
const cors = require('cors');
...
app.use(cors({origin: 'https://recipes.example.co'}))
//Upload Photo Endpoint
app.post('/uploadphoto', function (req, res) {
const s3 = new AWS.S3(); // Create a new instance of S3
const fileName = req.body.fileName;
const fileType = req.body.fileType;
const s3Params = {
Bucket: S3_BUCKET,
Key: fileName,
Expires: 500,
ContentType: fileType,
ACL: 'public-read'
};
s3.getSignedUrl('putObject', s3Params, (err, data) => {
if(err){
console.log(err);
res.json({success: false, error: err})
}
// Data payload of what we are sending back, the url of the signedRequest and a URL where we can access the content after its saved.
const returnData = {
signedRequest: data,
url: `https://${S3_BUCKET}.s3.amazonaws.com/${fileName}`
};
// Send it all back
res.json({success:true, data:{returnData}});
});
})
AddRecipeForm.js excerpt:
handleUpload = (ev) => {
console.log("handleUpload")
console.log(ev)
let file = this.uploadInput.files[0];
// Split the filename to get the name and type
let fileParts = this.uploadInput.files[0].name.split('.');
let fileName = fileParts[0];
let fileType = fileParts[1];
console.log("Preparing the upload");
axios.post("https://bqdu4plthq.execute-api.us-east-1.amazonaws.com/dev/uploadphoto",{
fileName : fileName,
fileType : fileType
})
.then(response => {
var returnData = response.data.data.returnData;
var signedRequest = returnData.signedRequest;
var url = returnData.url;
this.setState({url: url})
console.log("Recieved a signed request " + signedRequest);
// Put the fileType in the headers for the upload
var options = {
headers: {
'Content-Type': fileType
}
};
axios.put(signedRequest,file,options)
.then(result => {
console.log("Response from s3")
this.setState({success: true});
})
.catch(error => {
console.error(error);
})
})
.catch(error => {
console.error(error);
})
}
When clicking the Upload Photo button which calls the handleUpload funciton in AddRecipeForm.js, I get the following errors in my console:
Origin https://recipes.example.co is not allowed by Access-Control-Allow-Origin.
XMLHttpRequest cannot load https://bqdu4plthq.execute-api.us-east-1.amazonaws.com/dev/uploadphoto due to access control checks.
Failed to load resource: Origin https://recipes.example.co is not allowed by Access-Control-Allow-Origin.
Note that every other route works (getRecipe, allRecipes, addRecipe) and sends CORS headers, so I'm not sure why my addphoto request from React to API Gateway isn't sending CORS headers, even though it should be using it in index.js. Thanks in advance for help!