Making a request with Axios inside Promise.all - javascript

I have a script that updates the content of an API. There are several, so the only way is to call repeatedly to the endpoint to get the content of each API. When I am already inside the promise all, and I have the array of promises, what I do is iterating and updating the content that I have obtained.
Although the code seems to be working with an API only, I don't know what is the ideal way to make requests with Axios or fetch inside a promise.all or if just making the request is enough. Think that if for example there are 500 sequences, it will iterate through them and update their content.
The code is:
let promises = [];
data.sequences.forEach((sequence) => {
promises.push(axios.get(
`https://${conf.server.hostname}:${conf.server.port}/resource/${conf.version}/${sequence}`, {
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer bbbbbbb'
}
}
))
});
Promise.all(promises).
then((sequences) => {
//for each sequence to update
for (let i = 0; i < sequences.length; i++) {
let indexPromise = sequences[i];
axios.put(
`https://${conf.server.hostname}:${conf.server.port}/resource/${conf.version}/${sequenceName}`,
indexPromise.data, {
headers: {
'Content-Type': 'application/json'
}
}
).then((response) => {
logger.debug(`Updating content : ${sequenceName}`);
}).catch((err) => {
logger.error(`Error updating content`, err);
})
}
return callback(null, data)
}).catch((err) => {
logger.error(`Error getting content:`, err);
});

I would suggest this structure that makes the .get() and .put() into a combined operation and then runs Promise.all() once on the combined operations:
function someFunction() {
const corePath = `https://${conf.server.hostname}:${conf.server.port}/resource/${conf.version}`;
// return a promise that indicates when we're all done or had an error
return Promise.all(data.sequences.map(sequence => {
return axios.get(`${corePath}/${sequence}`, {
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer bbbbbbb'
}
}).then(data => {
return axios.put(`${corePath}/${sequenceName}`, data, {
headers: {
'Content-Type': 'application/json'
}
});
}).then(() => {
logger.debug(`Updating content : ${sequenceName}`);
}).catch(err => {
logger.error(`Error updating content`, err);
throw err;
});
});
}
Other notes:
Don't mix plain callbacks and promises. If you need to communicate back to some other code when this is done or has an error, then return your promise - don't use a callback.
You don't show where sequenceName comes from. Your debug output makes it seem like it's something that varies by request, but it isn't defined anywhere in the code in your question.
If you want the promise this is returning resolve with some data, then return that value from the final .then(). Your question shows you calling a callback and passing it data, but doesn't show where that comes from.

Related

Refactor from fetch to await that can yield same result

So I moved over a non-reusable fetch request code snippet to my API:
let response = await fetch(visitURL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + userJWT
},
body: JSON.stringify(endingVisit)
});
if (response.ok) {
let {visitId, createdAt} = await response.json();
const viewVisitDto = new ViewVisitDto(`${visitId}${createdAt}${visitorId}${doctorId}${oldPatientId}`);
return viewVisitDto;
} else {
throw new Error("deactivated!")
}
I was able to get this far:
axios.post(visitURL, {
headers,
body: JSON.stringify(visit)
}).then((response) => {
console.log(response);
}).catch((error) => {
console.log(error);
})
But does not exactly give me the visitId and createdAt from the response and I cannot use a response.ok nor a response.json(). Essentially I need to pull out that visitId and createdAt that should be coming back in the response.
I also tried just using node-fetch library, but although in VS code it seems to accept it, TypeScript is not happy with it even when I do install #types/node-fetch and even when I create a type definition file for it, my API just doesn't like it.
Guessing what you are after is
// don't know axios, but if it returns a promise await it
const dto = await axios.post(visitURL, {
headers,
body: JSON.stringify(visit)
}).then((response) => {
// parse response
return {resonse.visitId, resonse.createdAt}
}).then(({visitId, createdAt}) => {
// form dto (where are other vals)?
return new ViewVisitDto(`${visitId}${createdAt}${visitorId}${doctorId}${oldPatientId}`);
}).catch((error) => {
console.log(error);
})
However - you don't mention where doctorId and oldPatientId come from... You try providing more info, including output of the console.log's and the surrounding code

Fetch won't return any results

I have never used fetch before, and have followed the documentation, however, no results are being returned from my backend. When I submit the form, the url changes, and all appears fine in my console, but no response from my express backend.
The following code is what I have after my form in a script tag. Can someone please advise?
async function getSample(url = `http://localhost:3000/lookup/${url}`, data = {}) {
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
return response.json();
}
document.getElementById('search').addEventListener('submit', function(e) {
event.respondWith(
new Response(myBody, {
headers: {'Content-Type': 'application/json'}
})
);
});
You could try creating a promise and then handling the value returned by the fetch with resolve and reject
async function getSample(url = `http://localhost:3000/lookup/${url}`, data = {}){
return new Promise(function (resolve, reject) {
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).then(async response => {
if (response.ok) {
response.json().then(json => resolve(json));
} else {
response.json().then(json => reject(json));
};
}).catch(async error => {
reject(error);
});
});
};
You would then call it like
getSample(...)
.then(results => {
//Code when fetch is successful
}.catch(error => {
//Code when fetch fails
};
I think the problem with it returning nothing is that getSample is an async function, but I imagine you're calling it within a program that isn't async, and so whatever code comes after getSample is using trying to use the value returned from getSample, but nothing's been returned yet, so it's using an empty value. Either that or the return of getSample is happening before the fetch completes. I'm not sure of the exact order that things happen, but a promise should fix your problem

Javascript - Async await and fetch - return the value, not the promise?

I'm trying to work with async await and fetching data using the fetch api
My problem is, I just don't quite understand how I really get the answer from the server and not the status of the promise. I get the following in the console
PromiseĀ {<pending>}
-------------------------------
{locale: "en"}
but what I rather expect is the server response which should be "locale: en".
my code:
const locale = getLocale();
console.log(locale) // here I want to have "locale: en" as a output
async function fetchLocale() {
return await fetch('/get-language', {
headers: {
'Accept': 'application/json'
},
method: 'GET',
}).then(response => {
if (response.ok) {
return response.json()
}
return Promise.reject(Error('error'))
}).catch(error => {
return Promise.reject(Error(error.message))
})
}
async function getLocale() {
return await fetchLocale();
}
The goal I want to archive is, to return this "locale: en" response and store it in the "locale" const at the beginning of my code example.
KR
Your function should look more like this:
async function getLocale() {
let response = await fetch('/get-language', {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: 'GET',
});
// you can check for response.ok here, and literally just throw an error if you want
return await response.json();
}
And you can consume the result of that in an async function using await
const locale = await getLocale();
console.log(locale);
or by using the promise methods
getLocale().then(function(locale){
console.log(locale);
})
To get the value a Promise resolves with and not the promise itself, use .then() or await. Note that both require a callback or asynchronous context as it's simply not possible to bring this asynchronous result to a synchronous context such as the top level of your file.
getLocale().then(locale => {
console.log(locale);
//locale is only valid here
});

Is this a bad way to use ternary if else with promises in React

I am using ternary if else for calling different APIs(API calls are stored in another file).
(this.props.item === "burger" ? burgerObj.GetBurger (this.props.burger_id) : pizzaObj.GetPizza (this.props.pizza_id)) // burgerObj and pizzaObj are objects of their respective classes
.then((response) => {
if(response.status === 400) {
console.log ("Bad Request")
}
else {
response.json()
.then((findresponse) => {
this.prepareFood(findresponse) // Some function call
})
}
})
.catch ((e) => {
console.log (e)
})
}
// API calls in different classes. Same is for GetPizza
GetBurger(burger_id) {
return fetch(url + 'burgerId' + burger_id , {
headers: {
'Authorization': 'Bearer ' + authorization,
'Content-Type': 'application/json',
}
})
}
Is this a wrong way of handling such calls. Because when I'm using this API calling is working fine but the code never reaches promise (.then((response) => { - line no. 2). If I check Network I can see the response returned from API. Sorry if I am wrong somewhere I'm still new to React. Please help me.
Using Ternary is ok. But you can you can use Async/Await to clean up callbacks -
const { item, burger_id, pizza_id} = this.props;
function fetchWrapper(url) {
fetch(url, {
headers: {
Authorization: `Bearer${authorization}`,
'Content-Type': 'application/json',
}
})
.then(data => { return { data, error: null }; })
.catch(error => { return { error, data: null }; });
}
// API calls in different classes. Same is for GetPizza
async function GetBurgerAsync(burgerId) {
let response = await fetchWrapper(`url burgerId${burgerId}`);
const data = await response.json();
return data;
}
const foodType = item === 'burger' ? GetBurgerAsync(burger_id) : GetPizzaAsync(pizza_id);
if (foodType.data) {
this.prepareFood(foodType);
}
Edit: refactored, wrapping fetch as only url should change on every call. Token should be same across all calls.

How to get Readable error response from JavaScript Fetch API?

I am working on Reactjs redux on front-end and Rails API as a back-end.
So now I call API with Fetch API method but the problem is I cannot get readable error message like what I got inside the network tabs
this is my function
export function create_user(user,userInfoParams={}) {
return function (dispatch) {
dispatch(update_user(user));
return fetch(deafaultUrl + '/v1/users/',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(userInfoParams)
})
.then(function(response) {
console.log(response);
console.log(response.body);
console.log(response.message);
console.log(response.errors);
console.log(response.json());
dispatch(update_errors(response));
if (response.status >= 400) {
throw new Error("Bad response from server");
}
})
.then(function(json){
console.log("succeed json re");
// We can dispatch many times!
// Here, we update the app state with the results of the API call.
dispatch(update_user(json));
});
}
}
But when errors came I cannot figure out how to get readable response message like I got when I check on my browser network tabs
So this is what I got from the network tabs when I got errors.
My console
This is my rails code
def create
user = User.new(user_params)
if user.save
#UserMailer.account_activation(user).deliver_now
render json: user, status: 201
else
render json: { errors: user.errors }, status: 422
end
end
But I cannot find out how can I get that inside my function
Since the text is hidden inside promise within response object, it needs to be handled like a promise to see it.
fetch(bla)
.then(res => {
if(!res.ok) {
return res.text().then(text => { throw new Error(text) })
}
else {
return res.json();
}
})
.catch(err => {
console.log('caught it!',err);
});
Similar to your answer, but with a bit more explanation... I first check if the response is ok, and then generate the error from the response.text() only for the cases that we have a successful response. Thus, network errors (which are not ok) would still generate their own error without being converted to text. Then those errors are caught in the downstream catch.
Here is my solution - I pulled the core fetch function into a wrapper function:
const fetchJSON = (...args) => {
return fetch(...args)
.then(res => {
if(res.ok) {
return res.json()
}
return res.text().then(text => {throw new Error(text)})
})
}
Then when I use it, I define how to handle my response and errors as needed at that time:
fetchJSON(url, options)
.then((json) => {
// do things with the response, like setting state:
this.setState({ something: json })
})
.catch(error => {
// do things with the error, like logging them:
console.error(error)
})
even though this is a bit old question I'm going to chime in.
In the comments above there was this answer:
const fetchJSON = (...args) => {
return fetch(...args)
.then(res => {
if(res.ok) {
return res.json()
}
return res.text().then(text => {throw new Error(text)})
})
}
Sure, you can use it, but there is one important thing to bare in mind. If you return json from the rest api looking as {error: 'Something went wrong'}, the code return res.text().then(text => {throw new Error(text)}) displayed above will certainly work, but the res.text() actually returns the string. Yeah, you guessed it! Not only will the string contain the value but also the key merged together! This leaves you with nothing but to separate it somehow. Yuck!
Therefore, I propose a different solution.
fetch(`backend.com/login`, {
method: 'POST',
body: JSON.stringify({ email, password })
})
.then(response => {
if (response.ok) return response.json();
return response.json().then(response => {throw new Error(response.error)})
})
.then(response => { ...someAdditional code })
.catch(error => reject(error.message))
So let's break the code, the first then in particular.
.then(response => {
if (response.ok) return response.json();
return response.json().then(response => {throw new Error(response.error)})
})
If the response is okay (i.e. the server returns 2xx response), it returns another promise response.json() which is processed subsequently in the next then block.
Otherwise, I will AGAIN invoke response.json() method, but will also provide it with its own then block of code. There I will throw a new error. In this case, the response in the brackets throw new Error(response.error) is a standard javascript object and therefore I'll take the error from it.
As you can see, there is also the catch block of code at the very end, where you process the newly thrown error. (error.message <-- the error is an object consisting of many fields such as name or message. I am not using name in this particular instance. You are bound to have this knowledge anyway)
Tadaaa! Hope it helps!
I've been looking around this problem and has come across this post so thought that my answer would benefit someone in the future.
Have a lovely day!
Marek
If you came to this question while trying to find the issue because response.json() throws "Unexpected token at position..." and you can't find the issue with the JSON, then you can try this, basically getting the text and then parsing it
fetch(URL)
.then(async (response) => {
if (!response.ok) {
const text = await response.text()
throw new Error(text)
}
// Here first we convert the body to text
const text = await response.text()
// You can add a console.log(text), to see the response
// Return the JSON
return JSON.parse(text)
})
.catch((error) => console.log('Error:', error))
.then((response) => console.log(response))
I think you need to do something like this
export function create_user(user,userInfoParams={}) {
return function (dispatch) {
dispatch(update_user(user));
return fetch(deafaultUrl + '/v1/users/',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(userInfoParams)
})
.then(function(response) {
console.log(response);
console.log(response.body);
console.log(response.message);
console.log(response.errors);
console.log(response.json());
return response.json();
})
.then(function(object){
if (object.errors) {
dispatch(update_errors(response));
throw new Error(object.errors);
} else {
console.log("succeed json re");
dispatch(update_user(json));
}
})
.catch(function(error){
this.setState({ error })
})
}
}
You can access the error message with this way:
return fetch(deafaultUrl + '/v1/users/',
{
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
method: "POST",
body: JSON.stringify(userInfoParams)
})
.then(function(response) {
console.log(response);
console.log(response.body);
console.log(response.message);
console.log(response.errors);
console.log(response.json());
dispatch(update_errors(response));
if (response.status >= 400) {
throw new Error("Bad response from server");
}
})
.then(function(json){
console.log("succeed json re");
// We can dispatch many times!
// Here, we update the app state with the results of the API call.
dispatch(update_user(json));
})
// here's the way to access the error message
.catch(function(error) {
console.log(error.response.data.message)
})
;
The best choice is not to catch the error in the fetch because this will be useless:
Just in your api put a response with not code error
static GetInvoicesAllData = async (req,res) =>
{
try{
let pool = await new Connection().GetConnection()
let invoiceRepository = new InvoiceRepository(pool);
let result = await invoiceRepository.GetInvoicesAllData();
res.json(result.recordset);
}catch(error){
res.send(error);
}
}
Then you just catch the error like this to show the message in front end.
fetch(process.env.REACT_APP_NodeAPI+'/Invoices/AllData')
.then(respuesta=>respuesta.json())
.then((datosRespuesta)=>{
if(datosRespuesta.originalError== undefined)
{
this.setState({datosCargados:true, facturas:datosRespuesta})
}
else{ alert("Error: " + datosRespuesta.originalError.info.message ) }
})
With this you will get what you want.
You variables coming back are not in response.body or response.message.
You need to check for the errors attribute on the response object.
if(response.errors) {
console.error(response.errors)
}
Check here https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
You should actually be returning an error response code from the server and use the .catch() function of the fetch API
First you need to call json method on your response.
An example:
fetch(`${API_URL}`, {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(userInfoParams)
})
.then((response) => response.json())
.then((response) => console.log(response))
.catch((err) => {
console.log("error", err)
});
Let me know the console log if it didn't work for you.

Categories