POST request to DeepL api through node.js not working - node.js

Can anyone spot any problems that may explain why the api client is giving me the forbidden error? I know the credentials are correct, as GET requests w the same info in the url work find.
Thank you in advance
app.get('/translate', (req, res) => {
var textToTranslate = "Hello friend"
const targetLanguage = "ES"
var link = `https://api-free.deepl.com/v2/translate`
var options =
{
method: 'POST',
headers: {
"Host": 'api-free.deepl.com',
"Content-Length": 54,
"Content-Type": 'application/x-www-form-urlencoded',
"User-Agent": "YourApp",
"Accept": "*/*",
},
body: JSON.stringify({
'auth_key': deeplAccessCode,
'text': textToTranslate,
'target_lang': targetLanguage
}),
}
return fetch(link, options)
.then((response) => {
console.log(response)
return response.json(); //Transform http body to json
})
.then((json)=> {
res.send(json) //return json to browser
})
.catch(e => {
console.log(e)
return res.sendStatus(400);
});
})

It's probably failing because you're setting your Content-Type of your body to be application/x-www-form-urlencoded (which is correct as per the DeepL API specification) but then you provide a JSON body (which would require content type to be application/json).
You need to provide a URL-encoded body instead, like the part you can also append to the URL after the ?. See also this answer on SO.

Related

API call from https node application never reaches the destination

I have a node.js application served over https. I would like to call an API from that application. The API is also served over https and it has been generated using the express-generator.
Unfortunately the call never works. There is no error message. The call never reaches the API.
Strangely enough if I try to call another public API (e.g. https://api.publicapis.org/entries') that is working perfectly.
Here is my call:
const requestBody = {
'querystring': searchQuery,
};
const options = {
rejectUnauthorized: false,
keepAlive: false, // switch to true if you're making a lot of calls from this client
};
return new Promise(function (resolve, reject) {
const sslConfiguredAgent = new https.Agent(options);
const requestOptions = {
method: 'POST',
body: JSON.stringify(requestBody),
agent: sslConfiguredAgent,
redirect: 'follow',
};
fetch('https://192.168.112.34:3003/search', requestOptions)
.then(response => response.text())
.then(result => resolve(result))
.catch(error => console.log('error', error));
});
};
And here is the API which I would like to call:
router.post('/', cors(), async function(req, res, next) {
req.body;
queryString = req.body.querystring;
let data = JSON.stringify({
"query": {
"match": {
"phonetic": {
"query": queryString,
"fuzziness": "AUTO",
"operator": "and"
}
}
}
});
const { body } = await client.search({
index: 'phoneticindex',
body: data
});
res.send(body.hits.hits)
});
What is wrong with my API and/or the way I am trying to communicate with it?
UPDATE: I receive the following error in the fetch catch block: 'TypeError: Failed to fetch'
When I create a request in Postman I receive the expected response.
UPDATE 2: This is most probably an SSL related issue. The webapp is expecting an API with a valid certificate. Obviously my API can only have a self signed cert which is not enough here. How can I generate a valid cert for an API which is running on the local network and not publicly available?
UPDATE 3: I managed to make it work by changing the fetch parameters like this:
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
mode: 'cors',
body: raw,
agent: httpsAgent,
redirect: 'follow',
})
and on the API side I added the following headers:
'Content-Type': 'application/json',
'Access-Control-Allow-Origin' : 'https://localhost:2200',
'Access-Control-Allow-Methods' : 'POST',
'Access-Control-Allow-Headers' : 'Content-Type, Authorization'
I also added app.use(cors()) and regenerated the self-signed certificates.

UrlFetchApp Google Scripts VS Node Fetch

I'm having some problems to do a simple POST request with UrlFetchApp on Google Scripts.
This code works fine on NodeJS with node-fetch lib.
const fetch = require('node-fetch');
const URL = "https://login.XXXXXXXXXXXXX.com.br/api/login"
fetch(URL, {
"body": "{'my_json_data': 'login data'}",
"method": "POST",
}).then(res => res.text())
.then(body => console.log(JSON.parse(body)));
The same request on a Google Scripts project using UrlFetchApp give me a 403 Forbidden HTTP error.
var url = 'https://login.XXXXXXXXX.com.br/api/login';
var data = {
'email':'EMAIL',
'password':'PASS'
}
var options = {
method: 'POST',
payload: JSON.stringify(data)
}
var response = UrlFetchApp.fetch(url, options);
Logger.log(response)
What am I missing here?
Edit: Already tried with payload.
I believe your goal as follows.
You want to convert the following Node.js script to Google Apps Script.
const fetch = require('node-fetch');
const URL = "https://login.XXXXXXXXXXXXX.com.br/api/login"
fetch(URL, {
"body": "{'my_json_data': 'login data'}",
"method": "POST",
}).then(res => res.text())
.then(body => console.log(JSON.parse(body)));
Modification points:
In your Node.js script, the data of "{'my_json_data': 'login data'}" is sent to data as text/plain. So in this case, when UrlFetchApp is used, the content type is required to be set. Because when the content type is not set, the data is sent as form-data.
When above points are reflected to your script, it becomes as follows.
Modified script:
From:
var options = {
method: 'POST',
payload: JSON.stringify(data)
}
To:
var options = {
method: 'POST',
payload: JSON.stringify(data),
contentType: "text/plain"
}
If contentType: "text/plain" occurs an error, please modify it to contentType: "text/plain;charset=UTF-8".
Although I'm not sure about the specification of your API, I thought that contentType: "application/json" might be able to be also used. But this is my guess.
Reference:
Class UrlFetchApp

Difficulties with posting data to Google Firebase HTTP Functions

I am trying to POST some variables as part of req.body to a Firebase Cloud Function. I am using the modern fetch() syntax as following:
const { licenseCode } = this.state;
fetch('https://myAPI.com/inputLicense', {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({ licenseCode })
})
And here is how my Cloud function looks:
exports.inputLicense = functions.https.onRequest((request, response) => {
// const { licenseCode } = request.body
console.log(request.get('content-type'))
console.log('query', request.query)
console.log('body', request.body)
})
Unfortunately, all of the above logging produces empty objects or undefined in the case of the first line. In a usual Express setup, I know I need to use:
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
But don't know how to do it with cloud functions. Google claims in their Docs they parse my requests automatically, based on the header but I just can't get it to work.
Any help is more then welcome, thanks in advance.
It seems like you solved the problem, but for anyone else having a problem. In my case the problem was the firebase cloud function triggering on the cors preflight request which contains no body.
I solved it by removing the application/json content-type in the fetch call and decoding the json body manually in the cloud function
// on the client
fetch(functionURL + '/' + name, {
body: JSON.stringify({abc: 123}),
method: 'POST',
mode: 'cors',
})
// on the server
functions.https.onRequest(async (req, res) => {
const {abc} = JSON.parse(req.body)
res.set('content-type', 'application/json')
res.set('Access-Control-Allow-Origin', '*')
res.status(200)
res.send({result: abc})
})
I've done some tests and apparently the problem comes from the object destructuring.
The following should work:
const obj = { licenseCode: this.state };
fetch('https://myAPI.com/inputLicense', {
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(obj)
})

400 Bad request while fetching data in post call

I am running my React js web app in one port 3000.
For node server I am using 4000.
While calling fetch method it returns `400 Bad request'.
Error
POST http://localhost:4006/auth/admin 400 (Bad Request)
react code npm started in 3000 port
fetch('http://localhost:4000/auth/admin',
{ mode: 'no-cors',
method: "POST",
body: JSON.stringify({
username:"admin",
password:"1234"
}),
headers: {
"Content-Type": "application/json",
'Accept': 'application/json, text/plain, */*',
credentials: "omit", //
// "Content-Type": "application/x-www-form-urlencoded",
},
})
.then((response) => console.log(response));
node code running in 4000 port
const passport = require("passport");
const route = require("../constants/routeStrings");
const keys = require("../config/keys");
const processStatus = require("../constants/processStatus");
const success = {
status: processStatus.SUCCESS
};
const failute = {
status: processStatus.FAILURE
};
module.exports = app => {
app.post('/auth/admin', passport.authenticate("local"), (req, res) => {
res.send(success);
});
};
Do not stringify the body. Change from
body: JSON.stringify({
username:"admin",
password:"1234"
}),
to
body: {
username:"admin",
password:"1234"
},
The 400 response is raised by passport since it is unable to read your params. You need to tell your "node" app to parse them before your actual routes.
// Import body parser, you should read about this on their git to understand it fully
const parser = require('body-parser');
const urlencodedParser = parser.urlencoded({extended : false});
// before your routes
app.use(parser .json());
app.use(urlencodedParser) // This will parse your body and make it available for your routes to use
Then do your other calls.
Also, make sure that you are sending username and password keys, otherwise read the documentation on how to change these key names to something else
I suffered long hours, but I overcame it throw writing those lines of code blocks. I successfully send the request to the server's controller, hopefully yours: make it try.
First define a async function to make POST request:
async function _postData(url = '', data = {}) {
const response = await fetch(url, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
credentials: 'same-origin',
redirect: 'follow',
referrerPolicy: 'no-referrer',
headers: {
"Content-type": "application/json; charset=UTF-8"
},
body: JSON.stringify(data)
});
return response.json();
}
Now create a request JSON payload:
let requestPayload = {
propertyName1: 'property value1',
propertyName2: 'property value23',
propertyName3: 'property value',
So on
}
Note: Request model will be your desired model, what request payload you actually send.
Now make a request using this payload including your end point URL:
_postData('http://servername/example', requestPayload )
.then(json => {
console.log(json) // Handle success
})
.catch(err => {
console.log(err) // Handle errors
});
100% worked on my project.

POST request from React to Node.js

I don't know why this happens.
When I'm making a request to my server in Node.js and when it's GET then I can get a response. It looks like that:
fetch(config.apiUsersURL, {
method: "GET",
headers: {
"Content-Type": "application/json"
},
credentials: "same-origin",
mode: 'no-cors'
})
.then(res => this.setState({
isConected: true
}))
.catch(error => error);
When I'm requesting to the same url but with POST I'm getting nothing. Am I missing something?
const ObjToSend = { isReady: true };
fetch( config.apiUsersURL, {
method: 'POST',
mode: 'no-cors',
body: JSON.stringify(ObjToSend),
headers: {
"Content-Type": "application/json"
},
credentials: "same-origin",
mode: 'no-cors',
})
.then(res => res.json())
.then(r => this.setState({ questions: r }))
My endpoint looks like that:
let randomProblem2;
router.post('/', (req, resp) => {
resp.append('Access-Control-Allow-Origin', '*')
resp.append('Access-Control-Allow-Headers', 'Content-Type')
console.log("this shows if yes was clicked", req.body)
if(req.body.isReady){ //when clicked
randomProblem2 = problemManager.getRandomProblem();
randomize(randomProblem2, resp);
}
})
function randomize(randomProblem2, resp){
resp.json({
randomProblem : randomProblem2
}
)}
Since the mode you are using is no-cors, you cannot use javascript to access the response
Quoted below from MDN:
no-cors — Prevents the method from being anything other than HEAD, GET
or POST, and the headers from being anything other than simple
headers. If any ServiceWorkers intercept these requests, they may not
add or override any headers except for those that are simple headers.
In addition, JavaScript may not access any properties of the resulting
Response. This ensures that ServiceWorkers do not affect the semantics
of the Web and prevents security and privacy issues arising from
leaking data across domains.
Kindly check the MDN link below for the rest of mode options
https://developer.mozilla.org/en-US/docs/Web/API/Request/mode

Resources