React.js, Express.js and the dreaded CORS - javascript

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();
});

Related

how to get cookie in react passed from express js api (MERN stack)

I have an api in express js that stores token in cookie on the client-side (react). The cookie is generated only when the user logins into the site. For example, when I test the login api with the postman, the cookie is generated as expected like this:
But when I log in with react.js then no cookie is found in the browser. Looks like the cookie was not passed to the front end as the screenshot demonstrates below:
As we got an alert message this means express api is working perfectly without any error!!
Here is my index.js file on express js that includes cookie-parser middleware as well
require("dotenv").config();
const port = process.env.PORT || 5050;
const express = require("express");
const app = express();
const cors = require("cors");
const authRouter = require("./routes/auth");
var cookieParser = require('cookie-parser')
connect_db();
app.use(express.json());
app.use(cookieParser())
app.use(cors());
app.use("/" , authRouter);
app.listen(port , () => {
console.log("Server is running!!");
})
Code for setting up the cookie from express api only controller
const User = require("../models/user");
const jwt = require("jsonwebtoken");
const bcrypt = require('bcrypt')
const login = async (req, res) => {
const { email, password } = req.body;
try {
const checkDetails = await User.findOne({ email });
if (checkDetails) {
const { password: hashedPassword, token, username } = checkDetails;
bcrypt.compare(password, hashedPassword, function (err, matched) {
if (matched) {
res.cookie("token", token, { expires: new Date(Date.now() + (5 * 60000)) , httpOnly: true }).json({ "message": "You logged in sucessfully!" });
} else {
res.status(500).json({ "message": "Wrong password" });
}
});
} else {
res.status(500).json({ "message": "Wrong email" });
}
} catch (error) {
console.log(error.message);
}
}
Here is the react.js code that I am using to fetch data from api without using a proxy in package.json file
if (errors.length === 0) {
const isLogin = await fetch("http://localhost:5000/api/login", {
method: "POST",
body: JSON.stringify({ email, password }),
headers: {
"Content-Type": "application/json"
}
});
const res = await isLogin.json();
if(res) alert(res.message);
}
I want to get to know what is the reason behind this "getting cookie in postman but not in the browser". Do I need to use any react package?
The network tab screenshot might help you.
If I see in the network tab I get the same cookie, set among the other headers
To my understanding, fetch doesn't send requests with the cookies your browser has stored for that domain, and similarly, it doesn't store any cookies it receives in the response. This seems to be the expected behaviour of fetch.
To override this, try setting the credentials option when making the request, like so:
fetch(url, {
// ...
credentials: 'include'
})
or, alternatively:
fetch(url, {
// ...
credentials: 'same-origin'
})
You can read more about the differences between the two here.
I got my error resolved with two changings in my code
In front end just added credentials: 'include'
fetch(url, {
method : "POST"
body : body,
headers : headers,
credentials: 'include'
})
And in back end just replaced app.use(cors()); to
app.use(cors({ origin: 'http://localhost:3000', credentials: true, exposedHeaders: ['Set-Cookie', 'Date', 'ETag'] }))
That's it got resolved, Now I have cookies stored in my browser!!! Great. Thanks to this article:
https://www.anycodings.com/2022/01/react-app-express-server-set-cookie-not.html
during development i also faced same things, let me help you that how i solve it,
Firstly you use proxy in your react package.json, below private one:-
"private": true,
"proxy":"http://127.0.0.1:5000",
mention the same port on which your node server is running
Like:-
app.listen(5000,'127.0.0.1',()=>{
console.log('Server is Running');
});
above both must be on same , now react will run on port 3000 as usual but now we will create proxy to react So, react and node ports get connected on same with the help of proxy indirectly.
Now, when you will make GET or POST request from react then don't provide full URL, only provide the path on which you wants to get hit in backend and get response,
Example:-
React side on sending request, follow like this:-
const submitHandler=()=>{
axios.post('/api/loginuser',
{mobile:inputField.mobile,password:inputField.password})
.then((res)=>{
console.log(res);
})
.catch((err)=>{
console.log(err);
})
}
Node side where it will hit:-
app.post('/api/loginuser', async(req,res)=>{
//Your Code Stuff Here
res.send()
}
on both side same link should hit, it is very important
it will 100%.
don't forget to mention
on node main main where server is listening

Post request not working on plesk while working on localhost

What I'm trying to do:
I'm trying to make a api that saves images. I'd send a post request to the api with the image in body and it saves to the static folder 'public/images'.
Problem: I tried to do this on localhost and it works perfectly. I've got the cross-origin error's before but I've fixed them. After I hosted the api on plesk, it doesn't save the image, or send a error message back. I've checked the logs on plesk and it says that it received the post request. but it's not doing anything with it.
front-end code on client-side (hosted on plesk):
const formData = new FormData();
var fileField = document.querySelector('input[type="file"]');
formData.append('image', fileField.files[0]);
const result = await fetch('https://cdn.dhulun.com/upload25single', {
method: 'POST',
body: formData
}).then(res => res.json())
if (result.status === "ok") {
console.log("Success");
window.location.href = '/';
} else if (result.status === "error") {
console.log("error", result.error)
} else {
console.log("Something went wrong...")
}
back-end code on api (hosted on plesk):
const express = require('express');
const multer = require('multer');
const path = require('path');
const dotenv = require('dotenv');
const mongoose = require('mongoose');
const cors = require('cors')
dotenv.config();
// CONNECT TO DATABASE
async function connectDB() {
await mongoose.connect(process.env.DB_CONNECT,
{ useNewUrlParser: true, useUnifiedTopology: true },
() => {
console.log("Connected to Portal Base [Server]")
});
}
connectDB();
var Media = require('./models/media')
// INIT APP
const app = express();
app.use(cors())
// STATIC FILES
app.use(express.static(__dirname + '/public'));
app.use('/image', express.static('./public/images'))
// RULES
// INIT MULTER
// Storage Engine
const storage = multer.diskStorage({
destination: './public/images',
filename: (req, file, cb) => {
return cb(null, `${file.fieldname}_${Date.now()}${path.extname(file.originalname)}`)
}
})
const upload = multer({
storage: storage,
limits: { fileSize: 8000000 },
})
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html')
});
app.post('/upload25single', upload.single('image'), async(req, res) => {
var image_url = `http://localhost:2525/image/${req.file.filename}`;
console.log({
status: 'ok',
image_url: image_url,
})
res.json({
status: 'ok',
image_url: image_url,
});
/* var location = req.body.location;
var category = req.body.category;
var sourceby = req.body.sourceby;
var tags = req.body.tags;
const media = new Media({
url: image_url,
location: location,
category: category,
source_by: sourceby,
tags: tags
});
try {
const saved = await media.saved();
res.json({
status: 'ok',
image_url: image_url,
});
} catch (error) {
res.json({
status: 'error',
err: error,
});
}
*/
})
function errHandler(err, req, res, next) {
if (err instanceof multer.MulterError) {
res.json({
status: 'error',
err: err.message
})
}
}
// ERROR HANDLING
app.use(errHandler)
app.listen(process.env.PORT || 2525, () => console.log("Server Started..."));
I get this error on the browser console (hosted on plesk): Uncaught (in promise) SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
When i click the code line number in the log i got sent to this line:
console.log("Something went wrong...")
EDIT: I got suggested to turn res.json() to res.text() and i got this:
<p>iisnode encountered an error when processing the request.</p><pre style="background-color: eeeeee">HRESULT: 0x6d
HTTP status: 500
HTTP subStatus: 1013
HTTP reason: Internal Server Error</pre><p>You are receiving this HTTP 200 response because <a href=https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config>system.webServer/iisnode/#devErrorsEnabled</a> configuration setting is 'true'.</p><p>In addition to the log of stdout and stderr of the node.exe process, consider using <a href=http://tomasz.janczuk.org/2011/11/debug-nodejs-applications-on-windows.html>debugging</a> and <a href=http://tomasz.janczuk.org/2011/09/using-event-tracing-for-windows-to.html>ETW traces</a> to further diagnose the problem.</p><p>The last 64k of the output generated by the node.exe process to stderr is shown below:</p><pre style="background-color: eeeeee">(node:7696) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
Using Node.Js: Express & Multer

Serving NodeJS Server in a ReactJS project seperately

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')
);

Why is the fetch function saying, that I have to use absolute urls, even if I have set a proxy?

At the moment I am coding a Shopify application. I want to fetch all the products from my store in server.js but every time it outputs a message, that says that only absolute urls are supported. A registered Webhook should get all the products inside my shop.
Error: only absolute urls are supported
Here is my javascript (server.js)
const { default: proxy } = require('#shopify/koa-shopify-graphql-proxy');
const { ApiVersion } = require('#shopify/koa-shopify-graphql-proxy');
app.prepare().then(() => {
const server = new Koa();
const router = new Router();
server.use(session(server));
server.keys = [/** Shopify Keys */];
server.use(
createShopifyAuth({
/**
* Webhook
*/
}),
);
const webhook = receiveWebhook({ secret: SHOPIFY_API_SECRET_KEY });
router.post('/webhooks/products/create', webhook, async (ctx) => {
await fetch('/graphql', {
credentials: 'include',
body: allProducts
})
.then((data) => {
console.log(data)
})
.catch((err) => {
console.log(err)
})
console.log('received Webhook: ', ctx.state.webhook);
})
server.use(router.allowedMethods());
server.use(router.routes());
console.log(proxy({ version: ApiVersion.Unstable }))
server.use(proxy({ version: ApiVersion.Unstable }))
server.listen(port, () => {
console.log(`> Ready on localhost:${port}`)
})
})
I was using the example from the npm package shopify koa proxy link here
How can I send http request with the proxy I am using?
The issue is exactly what the error says, fetch requires absolute urls.
Whether you have a proxy or not is really irrelevant to the fetch api, it doesn't know about that.
Just give it an absolute URL

I'm getting a "githubClientID" is not defined when calling the "config" package using the GitHub API

I'm creating a feature that to retrieves GitHub user names via the GitHub API.
When sending the GET request from Postman, I got a server error that reads:
"Configuration property "githubClientId" is not defined"
Despite the fact that I called the config package with githubClientID defined in config/default.json, like this:
{
"mongoURI": "mongodb+srv://massas:oir#socialapp-2dg3r.mongodb.net/test?retryWrites=true&w=majority",
"jwtToken" : "oecret",
"githubClientId:": "ID",
"githubSecret": "SECRET"
}
// change the values for public viewing
Here's the code that's calling the API:
const express = require('express');
const request = require('request');
const config = require('config');
// #route GET api/profile/github/:username
// #desc Get user repos from username
// #acess Private
router.get('/github/:username', (req, res) => {
try {
const options = {
uri: `https://api.github.com/users/${
req.params.username
}/repos?per_page=5&sort=created:asc&client_id=${config.get(
'githubClientId'
)}&client_secret=${config.get('githubSecret')}`,
method: 'GET',
headers: {
'user-agent': 'node.js'
}
};
request(options, (error, response, body) => {
if (error) console.error(error);
if (response.statusCode !== 200) {
return res.status(404).json({
msg: 'No Github profile found'
});
}
res.json(JSON.parse(body));
});
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
How can I resolve this error?
1- You need to change the options object from:
const options = {
uri: `https://api.github.com/users/${
req.params.username
}/repos?per_page=5&sort=created:asc&client_id=${config.get(
'githubClientId'
)}&client_secret=${config.get('githubSecret')}`,
method: 'GET',
headers: {
'user-agent': 'node.js'
}
};
to:
const uri = encodeURI(
`https://api.github.com/users/${req.params.username}/repos?per_page=5&sort=created:asc`
);
const headers = {
'user-agent': 'node.js',
Authorization: `token ${config.get('githubToken')}`
};
const gitHubResponse = await axios.get(uri, { headers });
2- Make sure to add a default.json file in config folder with your GitHub secret access token
{ "githubToken": "<yoursecrectaccesstoken>" }
3- Install axios in the root of the project, if you didn't install it
npm i axios

Categories