Serving NodeJS Server in a ReactJS project seperately - javascript

I am making an API request inside of a redux action. That API request is blocked due to a CORS issue. To solve this I made a proxy Express NodeJS server on the same project, that listens to a different port.
This works great in development, on my local host. But in production(deployed on Heroku), I'm not able to serve the proxy server.
Part of the problem is I'm not sure what port to serve the proxy server in production, and how to serve it differently from the React App
My API call:
axios.get(`/api/item-info`, {
params: {
productId: productId,
variantId: variantId
}
})
.then(response => response.data)
.then(data =>
dispatch(checkItemInventoryQuantity(parseInt(data.variant.inventory_quantity)))
)
.catch(error => console.log(error));
My Express NodeJS Server:
const express = require('express');
const request = require('request');
const app = express();
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept'
);
next();
});
app.get('/api/item-info', (req, res) => {
const {productId, variantId } = req.query
const username = process.env.REACT_APP_SHOPIFY_API_KEY;
const password = process.env.REACT_APP_SHOPIFY_API_PASSWORD;
request(
{ url: `https://${username}:${password}#semi-aquatics.myshopify.com/admin/api/2020-04/products/${productId}/variants/${variantId}.json` },
(error, response, body) => {
if (error || response.statusCode !== 200) {
return res.status(500).json({ type: 'error', message: error.message });
}
res.json(JSON.parse(body));
}
)
});
app.listen(3001, () =>
console.log('Express server is running on localhost:3001')
);

Related

CORS error despite changing Access-Control-Allow-Methods with CORS middleware when deployed in Heroku

I'm running one client local server with "VSC's Live servers" on localhost:5500, and I'm running a remote server using heroku.
Here is the relevant code regarding the AJAX call client-side when submitting a form:
const xhr = new XMLHttpRequest();
xhr.open("POST", "https://git.heroku.com/morning-falls-52888.git");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JContactRequest);
xhr.onload = () => {
const status = xhr.status;
if (status >= 200 && status < 300 ) {
$("form").append("<p id='submit-message' style='text-align:center;'>Thank you for your submission!</p>");
} else if (status == 418 ) {
window.alert(`Error: ${xhr.status}\nCannot brew coffee, I am a teapot.`);
} else {
console.log(`Before append ${status} ready state: ${xhr.readystate}`);
$("form").append("<p id='submit-message' style='text-align:center;'>Submission Failed</p>");
console.log(`After append ${status} ready state: ${xhr.readystate}`);
}
}
And here is the code server side:
require('dotenv').config();
const express = require('express');
const app = express();
app.use(express.json());
const PORT = process.env.PORT ||4500;
const cors = require("cors");
app.use(cors());
app.listen(PORT, () => { console.log(`Listening on PORT: ${PORT}`) });
// app.use((req,res,next)=>{
// res.status(404).send('this is working');
// })
app.get('/', (req, res) => {
res.header("Access-Control-Allow-Methods", "GET,PUT,POST,DELETE,OPTIONS");
res.status(201).send();
console.log("received get request");
})
app.post('/', (req, res) => {
var body=req.body;
console.log(body);
for(let [key,value] of Object.entries(body)){
if(value==""){
res.status(400).send(`Empty Field in ${key}`);
res.end();
break;
}
}
var email = body.emailfield;
if (body.comment == "Can you brew coffee?") {
res.status(418).send();
} else {
res.status(200).send('message sent');
}
// calling the api
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: email,
from: 'd#gmail.com', // Use the email address or domain you verified above
templateId:'*************************************',
dynamic_template_data:body,
}
//ES6
sgMail
.send(msg)
.then(() => {}, error => {
console.error(error);
if (error.response) {
console.error(error.response.body)
console.log(process.env.SENDGRID_API_KEY)
}
});
});
The message I'm getting on my console is the following:
Access to XMLHttpRequest at 'https://git.heroku.com/morning-falls-52888.git' from origin 'http://127.0.0.1:5500' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I'm kind of stumped because I would have thought that app.use(cors()) would have access-control-allow-origin=*.
I'm still very new to this, so any help is mighty appreciated. Thanks!
EDIT I just wanted to add that this code was running fine when I was running the server locally on port 4500
xhr.open("POST", "https://git.heroku.com/morning-falls-52888.git");
You're making an HTTP request to Heroku's Git repo, not to your application running on Heroku (which will have a URL like https://APPLICATION_NAME.herokuapp.com/something)
The CORS permissions have to be granted for the URL you are making the request to, not one that is loosely adjacent to it.

CORS not working on specific domains in firebase cloud functions

const whitelist = ["http://mywebsite.com", "http://localhost:5000"]
const corsOptions = {
origin: function (origin, callback) {
if (whitelist.indexOf(origin) !== -1) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}
}
const cors = require('cors')(corsOptions);
exports.getUserProfile = functions.https.onRequest((req, res) => {
cors(req, res, () => {
try {
//... here we fetch the user profile and send it in response
} catch (err) {
res.json({
status: "error",
message: err
})
}
});
});
I'm using CORS in this way so I could only let my own website and localhost:5000 to make requests to the cloud functions that I'm using. localhost:5000 is for me so I could test stuff locally before I deploy the files.
Making a request from reqbin.com to my cloud function is working, why? Shouldn't it NOT work, since I've specified the domain to be either from mywebsite.com or localhost:5000?
Is there any other way to test stuff locally without having to include localhost:5000 in the whitelist? I'm using firebase serve to test, but I'm assuming this won't work since the requests won't be treated from mywebsite.com
This will help you by using origin from cors library directly.
const cors = require('cors')
const corsHandler = cors({
origin:["http://mywebsite.com", "http://localhost:5000"]
});
exports.getUserProfile = functions.https.onRequest((req, res) => {
corsHandler(req, res, async () => {
try {
//... here we fetch the user profile and send it in response
} catch (err) {
res.json({
status: "error",
message: err
})
}
});
});

React.js, Express.js and the dreaded CORS

I'm sorry to be posting yet another question about CORS but I just can't figure this one out.
I have a React app using an Express.js server (running on http://localhost:9001) to upload an image to a Google Cloud storage bucket. I keep getting a CORS error even though the image is uploaded successfully and this is preventing me from getting the image's URL returned. I don't really understand how I can get a CORS error even though the image is uploaded but that's what's happening.
I have configured CORS on the Google Cloud storage bucket as follows:
[
{
"origin": ["http://localhost:3000"],
"responseHeader": "*",
"method": ["POST"],
"maxAgeSeconds": 3600
}
]
When I inspect the CORS error I'm getting I see the following:
The origin is http://localhost:3000, so that's configured correctly and I'm using POST to upload the image so that should be allowed as well.
The function I've written to upload the image is as follows:
function postImage(file) {
const formData = new FormData();
formData.append('file', file);
fetch(`${window.location.protocol}//${window.location.hostname}:9001/uploads`, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
// headers: {
// 'Content-Type': 'multipart/form-data'
// },
body: formData
})
// .then((response) => response.json())
.then((response) => console.log('This is your data:', response.data))
.catch(error => {
console.error('There has been a problem uploading your image', error);
});
}
I've commented out the headers as including them kept throwing up a Multipart: Boundary not found error that I've seen others have an issue with and removing the headers setting hasn't caused any other issues.
I have a helper function on the Express server that uploads the image to the Google Cloud storage bucket:
const uploadImage = (file) => new Promise((resolve, reject) => {
const { originalname, buffer } = file
const blob = bucket.file(originalname.replace(/ /g, "_"))
const filetype = blob.name.split('.').pop()
const filename = `${uuidv4()}.${filetype}`
const blobStream = blob.createWriteStream({
resumable: false
})
blobStream.on('finish', () => {
const publicUrl = format(
`https://storage.googleapis.com/${bucket.name}/${filename}`
)
resolve(publicUrl)
})
.on('error', () => {
reject(`Unable to upload image, something went wrong`)
})
.end(buffer)
})
Here are the functions on my Express server:
import { typeDefs } from './graphql-schema'
import { ApolloServer } from 'apollo-server-express'
import express from 'express'
import neo4j from 'neo4j-driver'
import { makeAugmentedSchema } from 'neo4j-graphql-js'
import dotenv from 'dotenv'
import { initializeDatabase } from './initialize'
const bodyParser = require('body-parser')
const multer = require('multer')
const uploadImage = require('./helpers/helpers')
dotenv.config()
const app = express()
const schema = makeAugmentedSchema({
typeDefs,
config: {
query: {
exclude: ['RatingCount'],
},
mutation: {
exclude: ['RatingCount'],
},
},
})
const driver = neo4j.driver(
process.env.NEO4J_URI,
neo4j.auth.basic(
process.env.NEO4J_USER,
process.env.NEO4J_PASSWORD
),
{
encrypted: process.env.NEO4J_ENCRYPTED ? 'ENCRYPTION_ON' : 'ENCRYPTION_OFF',
}
)
const init = async (driver) => {
await initializeDatabase(driver)
}
init(driver)
const server = new ApolloServer({
context: { driver, neo4jDatabase: process.env.NEO4J_DATABASE },
schema: schema,
introspection: true,
playground: true,
})
// Specify host, port and path for GraphQL endpoint
const port = process.env.GRAPHQL_SERVER_PORT || 4001
const path = process.env.GRAPHQL_SERVER_PATH || '/graphql'
const host = process.env.GRAPHQL_SERVER_HOST || '0.0.0.0'
// Code for uploading files to Google Cloud
app.use((req, res, next, err) => {
console.error(err.stack)
res.header("Access-Control-Allow-Origin", "*");
res.type('multipart/form-data')
res.status(500).json({
error: err,
message: 'Internal server error!',
})
next()
})
const multerMid = multer({
storage: multer.memoryStorage(),
limits: {
// no larger than 5mb.
fileSize: 5 * 1024 * 1024,
},
})
app.disable('x-powered-by')
app.use(multerMid.single('file'))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.post('/uploads', async (req, res, next) => {
try {
const myFile = req.file
const imageUrl = await uploadImage(myFile)
res
.status(200)
.json({
message: "Upload was successful",
data: imageUrl
})
} catch (error) {
next(error)
}
})
server.applyMiddleware({ app, path })
app.listen({ host, port, path }, () => {
console.log(`GraphQL server ready at http://${host}:${port}${path}`)
})
app.listen(9001, () => {
console.log('Node.js -> GCP server now listening for requests!')
})
I've tried a lot of different things to get this working:
I've tried adding http://localhost:9001 to the CORS configuration, as well as other URLs
I've tried opening up all origins with "*" for
I've read through all the documentation [here][3]
I've tried following all the troubleshooting documentation Google has here
I've cleared my browser cache as I've seen that can cause the CORS errors to persist - see another post here
I've tried waiting over night for my Google Cloud CORS configuration to take effect as I've heard the configuration can take a bit of time to propagate
Despite all of this I'm still getting the CORS error but my upload is still working. I just need to clear the error so I can get the returned image URL.
You add cors to Google Cloud storage bucket but you forgot to add it to express server POST function. Or use it as global on your express server.
Try this on your express POST function:
res.header("Access-Control-Allow-Origin", "http://example.com");
Or
res.header("Access-Control-Allow-Origin", "*");
Or even better:
/* Headers */
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*"); // update to match the domain you will make the request from
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});

How to send a post request to mailchimp on express through react

I am trying to send a new membership from a form in react to my express server to add to the mailchimp memberlist I am getting a cors error and I don't know if I am missing any proxys. I want a user to be able to sign up in react and then it sends it to the mailchimp database
I have been able to get the members list but I am not allowed to post to it :
This is my express backend :
const express = require('express');
const Mailchimp = require('mailchimp-api-v3');
require('dotenv').config();
var request = require('superagent');
var mc_api_key = process.env.REACT_APP_MAILCHIMP_API;
var list_id = process.env.REACT_APP_LIST_ID;
const app = express();
const mailchimp = new Mailchimp(mc_api_key);
const port = process.env.PORT || 5000;
// console.log that your server is up and running
app.listen(port, () => console.log(`Listening on port ${port}`));
app.use((request, response, next) => {
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Headers", "Content-Type");
next();
});
// Routes
app.post('/signup', function (req, res) {
request
.post('https://' + 'us20' + '.api.mailchimp.com/3.0/lists/' + list_id + '/members/')
.set('Content-Type', 'application/json;charset=utf-8')
.set('Authorization', 'Basic ' + new Buffer('any:' + mc_api_key ).toString('base64'))
.send({
'email_address': req.body.email,
'status': 'subscribed',
'merge_fields': {
'FNAME': req.body.firstName,
'LNAME': req.body.lastName
}
})
.end(function(err, response) {
if (response.status < 300 || (response.status === 400 && response.body.title === "Member Exists")) {
res.send('Signed Up!');
} else {
res.send('Sign Up Failed :(');
}
});
});
This is where I am trying to fetch in react in my app.js file :
onSubmit = (e,email) => {
e.preventDefault()
this.setState({user:email})
fetch('http://localhost:5000/signup',{
method: 'POST',
headers: {
'Accept':'application/json',
'Content-type':'application/json'
},
body: JSON.stringify({email_address: email, status: 'subscribed'})
}).then(console.log)
};
when clicking submit I expect the members email address to be sent over to the mailchimp API instead I am getting this error :
Access to fetch at 'http://localhost:5000/signup' from origin
'http://localhost:3000' has been blocked by CORS policy: Response to
preflight request doesn't pass access control check: 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.
Try this.
app.use((request, response, next) => {
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Headers", "Content-Type");
next();
});
to
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "http://localhost:5000");
next();
});
Just enable CORS on your server.
Install cors
$ npm install --save cors
Enable cors in express:
const cors = require('cors');
const express = require('express');
const app = express();
app.use(cors());
More about cors here.

Redirect 302 does not work when I pass it from express to react

I have an Express Application and in my API, I send a 302 status to the client. The client uses React and AXIOS. It throws this error:
Access to XMLHttpRequest at 'https://sandbox.zarinpal.com/pg/StartPay/000000000000000000000000000000012807' (redirected from 'http://localhost:5000/api/payment/x/handle/accepted')
from origin 'null' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource
Here is my API
router.post('/x/handle/accepted', (req, res) => {
const uuidv4 = require('uuid/v4');
zarinpalLib.request(req.body.acamount, req.body.acemail, req.body.acphone, req.body.acdescription, uuidv4(), function (data) {
if (data.status) {
console.log('here ', data.url)
res.writeHeader(302, { 'Location': data.url });
res.end();
} else {
console.log('here2 ', data.code)
// res.render('error', {zpTitle: appConfig.appTitle, zpError: data.code});
res.status(500).send(data.code)
}
});
});
Here is the React part
if (inputValue === 'RLS') {
var data = {
acfirstname: 'xx',
aclastname: 'xx',
acemail: 'xx',
acphone: phoneNumber,
acamount: inputAmount,
acdescription: 'xx'
};
const res = await;
axios({ method: 'POST', url: `${END_POINT_URL}/api/payment/x/handle/accepted`, data });
console.log(res)
}
I use this middleware in my Express app for CORS:
var allowCrossDomain = function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Credentials', 'true');
res.header('Access-Control-Allow-Headers', 'Content-Type,x-phone-token,Location');
next();
}

Categories