Nested API requests in a route node/express - javascript

I'm very new to coding and this is my first post here. My learning project is a website that uses an external CRM to store client data from web forms.
I have the storage part working fine, but can't figure out how to retrieve the data and pass it to a rendered page.
I need a route to do 3 operations, each operation works properly on it's own, I just can't figure out how to nest them so they happen in order.
Get details of a deal from the CRM
var options = { method: 'GET',
url: 'https://crm.com/dev/api/opportunity/' + req.params.id,
headers:
{ 'cache-control': 'no-cache',
'content-type': 'application/json',
accept: 'application/json',
authorization: 'Basic xxx' },
json: true };
request(options, function (error, response, body) {
if (error) throw new Error(error);
return body.contact_ids;
});
this will return an array of client numbers associated with the deal.
Iterate through the client numbers to look up data from each client, and put to array. I have defined an empty array called data, outside the function scope to catch the results.
resultFromAboveRequest.forEach(function(id) {
var options = { method: 'GET',
url: 'https://crm.com/dev/api/contacts/' + Number(id),
headers:
{ 'cache-control': 'no-cache',
'content-type': 'application/json',
accept: 'application/json',
authorization: 'Basicxxx' },
json: true };
request(options, function (error, response, body) {
if (error) throw new Error(error);
data.push(body);
});
});
render the resultant data array on a page
res.render("./applicants/resume", {data: data});
I'm pretty sure this is a job for promises, however i just can't seem to get my head around the syntax. Any help would be appreciated and I apologise if the format of this question is amateurish or in some way inappropriate.

I would suggest using the request-promise library (which is a promise interface to the request library) and then using promises to manage the sequencing and error handling in a series of asynchronous operations. You can do that like this:
const rp = require('request-promise');
const options = {
method: 'GET',
url: 'https://crm.com/dev/api/opportunity/' + req.params.id,
headers: {
'cache-control': 'no-cache',
'content-type': 'application/json',
accept: 'application/json',
authorization: 'Basic xxx'
},
json: true
};
rp(options).then(body => {
return Promise.all(body.contact_ids.map(id => {
const options = {
method: 'GET',
url: 'https://crm.com/dev/api/contacts/' + Number(id),
headers: {
'cache-control': 'no-cache',
'content-type': 'application/json',
accept: 'application/json',
authorization: 'Basicxxx'
},
json: true
};
return rp(options);
}));
}).then(data => {
res.render("./applicants/resume", {data: data})
}).catch(err => {
console.log(err);
res.status(500).send("internal server error");
});
Here's a general description of the steps:
Load the request-promise library. Rather than taking a completion callback, it returns a promise that is resolved with the response body or rejected if there is an error.
Make the first request.
Use a .then() handler on the returned promise to get the result.
Process that result with .map().
In the .map() callback, return another promise from a call to request-promise for each data item. That means that .map() will return an array of promises.
Use Promise.all() on that array of promises to know when they are all done.
Return the promise from Promise.all() so that it is chained to the previous .then() handler for sequencing.
Then, in another .then() handler (which won't get called until both the previous operations are completely done), you will get the data from the .map() operation in proper order which you can use to call res.render().
Finally, add a .catch() to catch any errors in the promise chain (all errors there will propagate to this .catch() where you can send an error response).

Related

javascript fetch() works with breakpoints, but fails with TypeError when run normally

I'm trying to fetch() text/plain data from a remote service. If I place a breakpoint in the promise "then" chain, the text data from the server is available. Without the breakpoint, I get a fetch() exception.
I am using a prototype design pattern (see below). When I place a breakpoint in the "then" chain as shown below, the data from the remote service is successfully retrieved. Without the breakpoint, the catch() is executed and the error is:
TypeError: Failed to fetch
I'm totally stumped and would appreciate any help!
Note, the server (a python app) sends back html, with
self.send_header("Access-Control-Allow-Origin", "*")
Also, if I use Ajax (FWIW, it works). I'd like to get it working with fetch() however.
function Fetch(Msg) {
// Msg contains some info on how to construct the JSON message to transmit -- not relevant here.
this.help = `
The Fetch object specifies communication basics using
the fetch(...) mechanism.
`;
// some misc object vars...
}
Fetch.prototype = {
constructor: Fetch,
postData: async function (url = '', data = {}) {
const response = await fetch(url, {
method: 'POST,
mode: 'cors',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'text/plain',
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
// body data type must match "Content-Type" header
body: JSON.stringify(data)
});
return await response.text(); //
},
handleErrorsInResponse: function (response) {
var debug = new Debug("Fetch.handleErrorsInResponse");
debug.entering();
debug.leaving();
},
handleReponse: function (response) {
var debug = new Debug("Fetch.handleResponse");
debug.entering();
console.log(response);
debug.leaving();
},
handleErrorsInFetch: function (response) {
var debug = new Debug("Fetch.handleErrorsInFetch");
debug.entering();
console.log(response);
debug.leaving();
},
call: function (payload) {
this.postData(
'http://some.url/',
payload)
.then(this.handleErrorsInResponse) // If I place a breakpoint here it works!
.then(this.handleReponse)
.catch(this.handleErrorsInFetch);
},
}
// Ultimately called by something like
comms = new Fetch();
someData = {"key": someJSON};
comms.call(someData);
Remove the wait on the response.
Replace
return await response.text();
by
return response.text();

res.json() is undefined when mocking post request with fetch-mock and isomrphic-fetch

I'm using fetch-mock to test my client action creators in cases where there is an async call being made to the BE.
While all get requests are working well I'm having hard time doing the same to post and put requests.
Attached here a code example that if works I believe that my actual code will work as well.
I'm using import fetchMock from 'fetch-mock' for mocking the response and require('isomorphic-fetch') directly to replace the default fetch
I added some comments but I do get a response with status 200 (if I change the mocked response status to 400 I get it as well. The problem is that res.json() resulted with undefined instead of the mocked result body.
Using JSON.stringify is something that I used after not being able to make it work without it.
const responseBody = {response: 'data from the server'};
fetchMock.once('http://test.url', {
status: 200,
body: JSON.stringify(responseBody),
statusText: 'OK',
headers: {'Content-Type': 'application/json'},
sendAsJson: false
}, {method: 'POST'});
fetch('http://test.url',
{
method: 'post',
body: JSON.stringify({data: 'Sent payload'}),
headers : {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
})
.then(function (res) {
expect(res.status).toEqual(200); // Pass
res.json();
})
.then(function (json) {
console.log(json); // print undefine
expect(json).toEqual(responseBody); // Fail expected value to equal: {"response": "data from the server"} Received: undefined
done();
})
Mocking GET requests is working just fine
I also tried using it with fetchMock.post but had no luck
Would also appreciate if someone knows how I can test the post request sent payload as well (can't see any reference for that in the documentation)
In your first then, you don't have an explicit return, with the keyword return
If you don't do a return, the next then doesn't know the value. That's why your json is undefined.
For example:
var myInit = { method: 'GET', mode: 'cors', cache: 'default' };
fetch('https://randomuser.me/api/',myInit)
.then(function(res) {
return res.json()
})
.then(function(r) {
console.log(r)
})
So, for you:
const responseBody = {response: 'data from the server'};
fetchMock.once('http://test.url', {
status: 200,
body: JSON.stringify(responseBody),
statusText: 'OK',
headers: {'Content-Type': 'application/json'},
sendAsJson: false
}, {method: 'POST'});
fetch('http://test.url',
{
method: 'post',
body: JSON.stringify({data: 'Sent payload'}),
headers : {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
})
.then(function (res) {
expect(res.status).toEqual(200); // Pass
return res.json(); // return here
})
.then(function (json) {
console.log(json); // print undefine
expect(json).toEqual(responseBody); // Fail expected value to equal: {"response": "data from the server"} Received: undefined
done();
})

React Native - Fetch POST not working

I am having huge troubles getting my fetch POST calls to work on iOS. My standard Fetch calls work and the Fetch POST calls work fine on android but not iOS.
The error that comes up is "Possible Unhandled Promise Rejection (id: 0): Unexpected token < in JSON at position 0"
It actually saves the post data to my server but throws that error.
I tried debugging the network request using GLOBAL.XMLHttpRequest = GLOBAL.originalXMLHttpRequest || GLOBAL.XMLHttpRequest; before the API call coupled with using CORS in my chrome debug tools. From there I can see that it is making two post calls one after the other. The first one has type "OPTIONS" while the second one has type "POST". It should probably be noted that the call works in the App while using CORS and the line of code above.
I'm very confused and have exhausted all avenues.
My code im using for refrence is as follows.
return fetch(url,{
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).then((res) => res.json());
If JSON.stringify is not working, then try to use FormData.
import FormData from 'FormData';
var formData = new FormData();
formData.append('key1', 'value');
formData.append('key2', 'value');
let postData = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data'
},
body: formData
}
fetch(api_url, postData)
.then((response) => response.json())
.then((responseJson) => { console.log('response:', responseJson); })
.catch((error) => { console.error(error); });
You use the following code for POST request in react native easily. You need to only
replace the parameter name and value and your URL only.
var details = {
'userName': 'test#gmail.com',
'password': 'Password!',
'grant_type': 'password'
};
var formBody = [];
for (var property in details) {
var encodedKey = encodeURIComponent(property);
var encodedValue = encodeURIComponent(details[property]);
formBody.push(encodedKey + "=" + encodedValue);
}
formBody = formBody.join("&");
fetch('http://identity.azurewebsites.net' + '/token', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: formBody
}).
.then((response) => response.json())
.then((responseData) => {
console.log("Response:",responseData);
}).catch((error) => {
Alert.alert('problem while adding data');
})
.done();
I would guess the response you are receiving is in HTML. Try:
console.warn(xhr.responseText)
Then look at the response.
Also, IOS requires HTTPS.
Edit: Possible duplicate: "SyntaxError: Unexpected token < in JSON at position 0" in React App
Here is an example with date that works for me!
The trick was the "=" equal and "&" sign and has to be in a string format in the body object.
Find a way to create that string and pass it to the body.
====================================
fetch('/auth/newdate/', {
method: 'POST',
mode: 'cors',
redirect: 'follow',
body: "start="+start.toLocaleString()+"&end="+end.toLocaleString()+"",
headers: new Headers({
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"
})
}).then(function(response) {
/* handle response */
if(response.ok) {
response.json().then(function(json) {
let releasedate = json;
//sucess do something with places
console.log(releasedate);
});
} else {
console.log('Network failed with response ' + response.status + ': ' + response.statusText);
}
}).catch(function(resp){ console.log(resp) });
server node.js?
npm i cors --save
var cors = require('cors');
app.use(cors());
res.header("Access-Control-Allow-Origin: *");

Javascript fetch response cutoff

I'm starting to learn react-native and ran into some problems while using fetch on Android.
try {
let response = await fetch(REQUEST_URL, {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
***parameters***
})
});
let responseJson = await response;
if(responseJson){
// console.log(responseJson);
console.log(responseJson.text());
// console.log(responseJson.json());
}
} catch(error) {
console.error(error);
}
The request is sent correctly but the answer isn't shown in it's totality:
(**loads more data before**){"ID":"779","DESCRICAO":"ZXCVB","CLIENTENUMERO":"10133","CLIENTENOME":"Lda 1","TREGISTO":"2015\\/11\\/24 09:34:15","TTERMO":"","SITUACAO":"C","TIPO":"P","NOTIFICACOES":"email","NOTIFI_TAREFA":"","ESFORCOS_TOT":"4","TEMPOGASTO_TOT":"0:01:44","TEMPOGASTO_PES":"0:01:44","PROJECTO":"New Products","USERNAME":"AT","UREGISTO":"S","EMCURSO":"0","TULTIMO":"2015\\/12\\/18 20:37:56","EQUIPA":"","NIVEL":"AVISAX"},{"ID":"783","DESCRICAO":"123","CLIENTENUMERO":"10133","CLIENTENOME":"Lda 1","TREGISTO":"2015\\/11\\/24 09:43:26","TTERMO":"","SITUACAO":"C","TIPO":"P","NOTIFICAC
As you can see, the JSON object isn't complete. Sending the same request using other methods in a browser returns the JSON correctly.
I'm wondering if this is an actual issue with fetch or with Android.
I've tried setting size and timeout parameters to 0 in fetch but it did nothing.
Edit: also tried using synchronous fetch instead of async, with the same effect:
fetch(REQUEST_URL, {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
***params***
})
})
.then((response) => response.text())
.then((responseText) => {
console.log(responseText);
})
.catch((error) => {
console.warn(error);
}
Also tried:
console.log(responseJson);
and
console.log(responseJson.json());
Edit for further clarification:
When using response.json(), the response is shown as json (as to be expected) but it's still incomplete.
Edit :: Issue was with console.log limiting the number of characters it displays in the console.
Quick question:
Can you get the json object in its entirety if you hit the endpoint with postman? It could very well be your server/service that is cutting off the message.
Lastly, (and I see you mentioned this above) but I always use the 'json' method off the response obj when I know that is the notation type - which should return a promise.
fetch(REQUEST_URL, {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
***params***
})
})
//get the response and execute .json
.then((r) => r.json())
//then listen for the json promise
.then((j) => {
console.log(j);
})
.catch((error) => {
console.warn(error);
}
Let me know what happens and if you get the full response with postman (or fiddler compose).

AngularJS HTTP error codes with $q.all()?

I have a bunch of http requests like this:
$q.all([$http({
method: 'POST',
url: urlOne,
headers: {Authorization: "Token " + jqToken}
}), $http({
method: 'POST',
url: urlTwo,
headers: {Authorization: "Token " + jqToken}
})])
.then(function (results) {
//do stuff
});
However urlOne and urlTwo (and a bunch of others) may under some conditions return 403. In this case everything just freezes and then() function is never executed. How can I handle 403 responses?
Thanks.
It sounds like you need to handle errors.
$q.all([...])
.then(
function (results) {
// Handle success
}, function (err) {
// Handle errors
});

Categories

Resources