Fetch problem with node.js express server - javascript

I'm having some trouble with the fetch and node.js.
In my frontend when i click a button, i would like to send a post request in order to receive an array from my backend as answer. I'n my backend i'm using node.js with express, in my frontend i'm using the fetch function.
The error that occours is the following:
Access to fetch at 'http://localhost:8080/api' from origin 'real localhost address' 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.
Code Here
const getArray = async() => {
const data = await fetch ("http://localhost:8080/api");
const dataJson = await data.json();
console.log(dataJson)
}
getArray();
In my server i've got
app.post("/api", (req,res) => {
res.sendFile(JSON.stringify(arr));
});

You need to add request options. Please refer to the MDN docs for further information.

As #Kudah said, you should read the docs.
Fetch (and XMLHttpRequest) follow the same-origin policy. This means that browsers restrict cross-origin HTTP requests from within scripts. A cross-origin request occurs when one domain (for example http://example2.com/) requests a resource from a separate domain (for example http://example1.com/).
The easiest way to solve this, (If you don't want to dig too much into this)
const whiteList = [ "https://myRealBackendUrl-1", "https://myRealBackendUrl-2" ];
// you can also pass a string here instead here instead of array
const corsOptions = {
credentials: true,
origin: process.env.NODE_ENV !== production ? "http://localhost:4000" : whiteList
// if you are in a dev environment, you probably want something like localhost
// http://localhost:4000 is just a demo backend. replace it with your own.
// if you are in a production environment, for example heroku then your backend
// url will be something like http://example.herokuapp.com
// in that case `const whiteList = [ "http://example.herokuapp.com" ];`
};
app.use(cors(corsOptions));
The above code should be enough for the normal use case.
There is also callback function, it is if you want to run some function of your own. Don't read it if you dont plan to use any dynamic checking
var corsOptionsDelegate = async (req, callback) => {
var corsOptions = { origin: false };
try {
// you can do some dynamic check here
// For example: check database for some conditions then allow access
if( myDatabaseSays == true ) corsOptions.origin = true;
else corsOptions.origin = false;
} catch (err) {
console.log(err);
// corsOptions.origin = false;
}
callback(null, corsOptions) // chain it
}
Anyway read the docs properly for more info
[1]: https://expressjs.com/en/resources/middleware/cors.html

Related

How do I set the origin in the DeepL API on javascript?

What else should I try?
I'm currently sending a request to the DeepL API in axios, but I'm getting a 403 response due to a CORS issue.
And tried to set the option using querystring as shown here, but it didn't work. https://github.com/funkyremi/deepl/blob/master/index.ts
Also, using the library at the URL above returns 403.
Furthermore, there is no origin setting in the account settings of DeepL.
I tried using 'Content-Type': 'application/x-www-form-urlencoded' for axios headers: {}, and I also tried setting options for params: { } and not using querystring, but they didn't work.
import axios from 'axios'
import querystring from 'querystring';
export const translateDeepL = async() => {
const options = {
"auth_key": process.env.DEEPL_AUTH_KEY,
"text": 'everyday is birthday.',
"target_lang": 'JA',
};
const url = "https://api-free.deepl.com/v2/translate";
const data = await axios.post(url, querystring.stringify(options)).then(r => r);
console.log(data);
}
VM3451:1 POST https://api-free.deepl.com/v2/translate 403
the request use https with ngrok did't work also.
I also tried the GET method for "https://api-free.deepl.com/v2/usage" but got the same result.
It is definitely api-free.deepl.com since I am using the free plan.
By the way, the above code is executed as a component in React.
the DeepL API does not support being used directly from within browser-based apps. The API Key is not supposed to be shared publicly as well and should always be kept secret.
The best approach is to use a backend proxy for the API Calls.
I was encountering this same issue and couldn't find an answer. This API just didn't seem to want to talk to me via a browser.
My 'solution' was to set up an API proxy in node.
It works fine fetching from a back-end + now I can add some rate limiting etc
C.J on coding garden can explain this way better than I ever can.
You might be being blocked because of sending a request from http (your localhost) to https, try using the proxy axios config, like
const response = await axios
.get("https://api-free.deepl.com/v2/translate", {
params: {
auth_key: x,
text: y,
target_lang: z
},
proxy: {
host: "localhost",
port: 8080
}
});
return response;
};

Route that is executed within another route in Node.js is not being executed

Good Evening,
I have a function that contains a route that is a call to the Auth0 API and contains the updated data that was sent from the client. The function runs, but the app.patch() does not seem to run and I am not sure what I am missing.
function updateUser(val) {
app.patch(`https://${process.env.AUTH0_BASE_URL}/api/v2/users/${val.id}`,(res) => {
console.log(val);
res.header('Authorization: Bearer <insert token>)
res.json(val);
})
app.post('/updateuser', (req, ) => {
const val = req.body;
updateUser(val);
})
app.patch() does NOT send an outgoing request to another server. Instead, it registers a listener for incoming PATCH requests. It does not appear from your comments that that is what you want to do.
To send a PATCH request to another server, you need to use a library that is designed for sending http requests. There's a low level library built into the nodejs http module which you could use an http.request() to construct a PATCH request with, but it's generally a lot easier to use a higher level library such as any of them listed here.
My favorite in that list is the got() library, but many in that list are popular and used widely.
Using the got() library, you would send a PATCH request like this:
const got = require('got');
const options = {
headers: {Authorization: `Bearer ${someToken}`},
body: someData
};
const url = `https://${process.env.AUTH0_BASE_URL}/api/v2/users/${val.id}`;
got.patch(url, options).then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
Note: The PATCH request needs body data (the same that a POST needs body data)

Api is not fetching in reactJs

I am trying to fetch food by its key. In postman api is working fine but is the forntend it has no response.
backend code
app.get('/foods/:key', (req, res) => {
foodsCollection.find({ key: req.params.key }).toArray((err, documents) => {
res.send(documents[0])
})
})
frontend code
const { key } = useParams()
const [foodById, setFoodById] = useState({})
useEffect(() => {
fetch(`http://localhost:5000/foods/${key}`)
.then((res) => res.json())
.then((data) => {
setFoodById(data)
})
}, [key])
Although you've added some images above, the most important is missing, namely, what are the Browser's Developer Tools stating the problem is. You should see some message in the Console tab, as well as in the Network tab for that particular request, if it is indeed being made. Until anyone sees this, it will be very difficult to help in fixing your problem.
If your not already, I suggest scaffolding any react app with create-react-app (CRA). This will give you a working app to start from. You can ignore CORS related issues in development, if using CRA, by adding "proxy": "http://localhost:5000", to your package.json file, see here for more on this method, but remember, this is only works for local development. You can also start Chrome to ignore Web Security by running it with the --disable-web-security flag e.g. chromium --disable-web-security, but that isn't a great idea really, more a way to quickly determine if you are having CORS problems, as Chrome masks some problems as CORS related, when in fact they aren't.
I'd also suggest changing your fetch code to use await, so instead you'd have:
const response = await fetch(`http://localhost:5000/foods/${key}`);
if (!response.ok) {
console.error(`Error message: ${response.statusText} ${response.status}`);
}
const result = response.json();
console.log(result);
This isn't necessary, but I've always found it way easier to read than the then/catch/finally method.
Reason for error
You need to stringify an object before sending it to the client with the JSON.stringify() method. When we exchange data to/from a web server, it must be a string.
Solution:
Proper way to send response to the client would to wrap the entire API in a try-catch block and explicitly specify the HTTP Status Code along with the stringified data in every response.
Note: Although 500 status code is used for error handling, you should choose one as per the use case.
app.get('/foods/:key', (req, res) => {
try {
/*
rest of the code
*/
foodsCollection.find({ key: req.params.key }).toArray((err, documents) => {
if (err) {
// 500 stands for internal server error
return res.status(500).send(JSON.stringify('Here goes a meaningful error message!'));
}
// 200 stands for success
res.status(200).send(JSON.stringify(documents[0]));
});
/*
rest of the code
*/
} catch (error) {
// 500 stands for internal server error
res.status(500).send(JSON.stringify('Here goes another meaningful error message!'));
}
})
The problem is that you haven't set the CORS headers of response in your backend code. and you are using different ports in your backend and frontend (5000 & 3000) so the Same Origin Policy disallows reading the remote resource, indicating that the request was blocked due to violating the CORS security rules.
you've to set the CORS headers.
you can install the CORS npm package and follow it's instructions to resolve the issue like this:
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors())
.
.
.
And one other issue that I'm seeing is that you've put the react-router default route before your specified path. so move the <route path="*"> after <route path="/foods/:key">

Azure Chatbot Token Server

I have an azure chat bot and I use it per direct line channel.
It is working fine if I use the secret directly in the HTML, but due to safety reasons I want to use Tokens. Thats why I used that:
<script>
window
.fetch('http://XXXXXXXX.azurewebsites.net/token-generate',
{
method: 'POST'
})
.then(function(res) {
return res.json();
})
.then(function(json) {
const token = json.token;
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({
token: token
})
},
document.getElementById('webchat'));
document.querySelector('#webchat > *').focus();
});
</script>
It is like that and not with an async function because it needs to work on IE11 too.
My index.js in my bot looks like this:
// Create HTTP server
const server = restify.createServer({
name: 'token-server'
});
server.listen(process.env.port || process.env.PORT || 3978, function() {
console.log(`\n${ server.name } listening to ${ server.url }`);
console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
console.log('\nTo talk to your bot, open the emulator select "Open Bot"');
});
server.post('/token-generate', async (_, res) => {
console.log('requesting token ');
res.setHeader('Access-Control-Allow-Origin', '*');
console.log(res);
try {
const cres = await fetch('https://directline.botframework.com/v3/directline/tokens/generate', {
headers: {
authorization: `Bearer ${ process.env.DIRECT_LINE_SECRET }`
},
method: 'POST'
});
// console.log(cres);
const json = await cres.json();
// console.log(json);
// json.header.append('Access-Control-Allow-Origin: *');
console.log(json);
if ('error' in json) {
res.send(500);
} else {
res.send(json);
}
} catch (err) {
res.send(500);
}
});
That is some code I found after some research how to use tokens to render the Webchat.
My problem is, that when I use this html code, I get some Errors:
Access to fetch at 'http://compliancebotbbraun-bot.azurewebsites.net/token-generate' from origin 'null' 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.
testbot.html:1 Uncaught (in promise) TypeError: Failed to fetch
and I just don't know how to change the Access-Control-Allow-Origin header. I don't find anything online and if I find something, it is not even close to my code.
It is working exactly as I tought it would work in IE11, but in Chrome,Edge and Firefox (idk for others, only tested these) these Errors are occuring.
I hope someone here can help me.
Based on my understanding , you exposed an API to grant access tokens to your bot clients by post method to your bot clients. Your bot clients use JS script to invoke this API . As you are using post method, so your bot clients will encounter CORS issues .
Based on the host of /token-generate url , this API is hosted on Azure webapp , you can just refer to this doc to define allowed domains to call this API from a static page by JS on Azure portal directly.
You can find the Azure webapp which hostes your API code here :
And open CORS settings here :
If you are just testing your bot from local static html file , adding "*" and remove other domains in CORS config will solve this issue .
Test result :
Hope it helps . If you have any further concerns , pls feel free to let me know .

express-http-proxy still gets blocked by CORS policy

I have an express server statically serving my Polymer project. I have a REST API query that I need to make, but if I make it from the client it will be blocked by CORS. So I used express-http-proxy to try to get around that; I send my request to it, and it redirects to the server that has the REST API endpoint on it. This is the entirety of my server code that's running with node server.js:
var express = require('express');
var proxy = require('express-http-proxy');
var server = express();
server.use('/', express.static(__dirname + '/'));
server.listen(8080);
server.use('/rest/api/2/search', proxy('restserver:8877'));
console.log("Server listening on localhost:8080");
When I access restserver:8877/rest/api/2/search in a browser it returns a bunch of json as a 'default' search.
On the client side, I have iron-ajax making this request:
<iron-ajax
id="getBugs"
url="/rest/api/2/search"
params=''
on-response="handleResponse"
debounce-duration="300">
</iron-ajax>
And in the script section, I'm using this.$.getBugs.generateRequest() in the ready function to send the request. So I load this up, expecting the request to not be blocked by CORS, since... it's being proxied by the server. Instead, Chrome devtools gives me this:
XMLHttpRequest cannot load http://restserver:8877/secure/MyJiraHome.jspa. Redirect from 'http://restserver:8877/secure/MyJiraHome.jspa' to 'http://restserver:8877/secure/Dashboard.jspa' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.
I don't understand why it's giving me those URLs, since I never reference them, and why it's blocking due to CORS, since it's going from the server, not the client, that being the whole point of the proxy.
It may be that express-http-proxy simply forwards the Origin header coming from your client which is http://localhost:8080, causing the end server to reject it.
Try modifying it with proxyReqOptDecorator:
server.use('/rest/api/2/search', proxy('restserver:8877', {
proxyReqOptDecorator(proxyReqOpts) {
proxyReqOpts.headers['Origin'] = 'http://accepted.origin.com';
return proxyReqOpts;
}
}));
Never used express-http-proxy and did not test it tho, so tell me if it's not a solution. Also I think using cors as other people suggested may simplify things a lot. But I don't know your development constraints, so I could be wrong.
Server is probably returning a 302 redirect which is not handled correctly in the used middleware.
Read more how the redirect works: https://en.wikipedia.org/wiki/HTTP_location
You can modify the Location response header to overcome the CORS issue or you can try:
var proxy = require('http-proxy-middleware');
var restServerProxy = proxy({target: 'http://restserver:8877', autoRewrite: true});
server.use('/rest/api/2/search', restServerProxy);
The above example should handle redirects automatically.
You don't need any proxy. Since you are calling endpoint on your server, you can just whitelist your client side for calling your server. You can do that with cors package.
https://www.npmjs.com/package/cors
First, define your CORS policy logic in one file (let's name it cors-policy-logic.js), and then export it so you can use it in other files.
const cors = require('cors');
const whitelist = ['http://localhost:8080', 'http://localhost:your_client_url'];
var corsOptionsDelegate = (req, callback) => {
var corsOptions;
if (whitelist.indexOf(req.header('Origin')) !== -1) {
corsOptions = { origin: true };
} else {
corsOptions = { origin: false };
}
callback(null, corsOptions);
};
exports.cors = cors();
exports.corsWithOptions = cors(corsOptionsDelegate);
Now, import it and use it anywhere were you define some endpoint:
var express = require('express');
const cors = require('./cors-policy-logic.js');
var server = express();
server.use('/', express.static(__dirname + '/'));
server.listen(8080);
server.route('/rest/api/2/search')
.options(cors.corsWithOptions, (req, res) => { res.sendStatus(200); })
.get(cors.cors, (req, res, next) => {
//Your business logic
});
console.log("Server listening on localhost:8080");
Alternative solution would be to use http-proxy-middleware as mentioned by #chimurai.
If you want to proxy to an https server to avoid CORS:
const proxy = require('http-proxy-middleware');
app.use('/proxy', proxy({
pathRewrite: {
'^/proxy/': '/'
},
target: 'https://server.com',
secure: false
}));
Here secure: false needs to be set to avoid UNABLE_TO_VERIFY_LEAF_SIGNATURE error.

Categories