How to use Fetch queries in a loop? - javascript

I make a request to the server via a map with different urls, then I set the data in State and use it for output. I want the requests to be consecutive but sometimes they do not work correctly and get bugs, how to write the code for normal data retrieval?
const urlList = ["countries", "states", "cities", "users"];
componentDidMount() {
urlList.map( (url, index) => {
return servicesAPI.getResourse(url).then( (body) => {
index !== 3 ? this.setState({
dataAPI : [...this.state.dataAPI, body] }) :
this.setState({
dataAPI : [...this.state.dataAPI, body],
loaded: true
})
})
})
export default class ServicesAPI {
_apiBase = `http://localhost:3001/`;
async getResourse(url) {
const res = await fetch(`${this._apiBase}${url}`);
if (!res.ok) {
throw new Error(`Could not fetch ${url}` +
`, received ${res.status}`)
}
return await res.json();
}

Use of Promise.all();
componentDidMount() {
const fetchPromises = [];
urlList.forEach( (url, index) => {
fetchPromises.push(servicesAPI.getResourse(url));
});
const allResourcesPromise = Promise.all(fetchPromises);
allResourcesPromise.then(data => {
// list with responses
}).catch(err => {
console.log(err.toString());
});
}
Sample example:
https://jsbin.com/vevufumano/1/edit?html,js,console,output
Also instead of then, where is possible, you can use async/await for more cleaner code.

Related

store multiple api calls in one react state

I have multiple api's that are returning same type of data but from different areas but the data type is the same only the values are different, and I want to store them all in one react state.
So I have this state:
let [infoData1, setInfoData1] = useState({ infoData1: [] });
let [infoData2, setInfoData2] = useState({ infoData2: [] });
and the axios calls :
function multipleApiCall() {
const headers = {
"X-Api-Key": "the-api-key-00",
};
axios.get(
"http:url-to-data/ID1",
{ headers }
)
.then((response) => {
setInfoData1(response.data);
return axios.get(
"http:url-to-data/ID2",
{ headers }
)
})
.then(response => {
setInfoData2(response.data);
})
}
and afterward I want to use a .map() to list the result but because I have 2 states I cannot concatenate them. So how can I have all data from those two api's in just one state or maybe another approach ?
const [infoData, setInfoData] = useState([]);
const headers = {
"X-Api-Key": "the-api-key-00",
};
const urls = ["http:url-to-data/ID1", "http:url-to-data/ID2"];
function multipleApiCall() {
const promises = urls.map(url => axios.get(url, { headers }));
Promise.all(promises).then(responses => {
let data = [];
responses.forEach(response => {
data = data.concat(response.data);
});
setInfoData(data);
});
}

How can I "encapsulate" this code into a module so it could become reusable?

I have got this Node.JS snippet and would like to write it as a module, so I can use recaptcha in different parts of my system.
This is how it currently looks like:
app.post('/register_user', (req, res) => {
const secret_key = process.env.RECAPTCHA_SECRET;
const token = req.body.recaptcha;
const url = `https://www.google.com/recaptcha/api/siteverify?secret=${secret_key}&response=${token}`;
fetch(url, { method: "post",})
.then((response) => response.json())
.then((google_response) => {
if (google_response.success == true) {
res.format({'text/html': () => res.redirect(303, '/register'),})
} else {
return res.send({ response: "Failed" });
}
})
.catch((error) => {
return res.json({ error });
});
})
I have tried to write the following module which works absolutely great, but I have absolute no idea about how to call it from the app.post, since I always get undefined as return:
import fetch from 'node-fetch';
export function fetch_out(url, timeout = 7000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), timeout)
)
]);
}
export async function checkRecaptcha(token, secret_key){
const url = "https://www.google.com/recaptcha/api/siteverify?secret=" + secret_key + "&response=" + token;
try{
const response = await fetch_out(url, 1000);
const google_response = await response.json();
}catch(error){
return error;
}
return google_response;
}
Any help would be appreciated! Thanks!
You could make this method reusable by removing the framework actions that need to happen and only return if the validation was successful or not. This way, it will be reusable in another project that doesn't use a specific framework.
Example module;
export async function checkRecaptcha(token, secret_key) {
const url = `https://www.google.com/recaptcha/api/siteverify?secret=${secret_key}&response=${token}`;
const response = await fetch(url, { method: "post",});
if (!response.ok) return false;
const json = await response.json();
if (!json.success) return false;
return true;
}
Usage:
import { checkRecaptcha } from "./some-file-name";
app.post('/register_user', async (req, res) => {
const isHuman = await checkRecaptcha(req.body.recaptcha, process.env.RECAPTCHA_SECRET);
if (!isHuman) {
return res.send({ response: "Failed" });
}
return res.format({'text/html': () => res.redirect(303, '/register'),});
});
If you specifically want to call an action after the validation, you can also use successful and error callbacks.

How to use foreach and promise

I need to get datas with nested foreach, but I can't fill my array.
At the end of this code I would like to have an array (segId) with my datas but it is empty (because of aynschronous).
I read that I had to use Promise.all but I can't beacause my promise are nested
I'm beginner so my code is far from perfect
How can I do that ?
async function getActivities(strava, accessToken)
{
const payload = await strava.athlete.listActivities({'access_token':accessToken, 'after':'1595281514', 'per_page':'10'})
return payload;
}
async function getActivity(strava, accessToken, id)
{
const payload = await strava.activities.get({'access_token':accessToken, 'id':id, 'include_all_efforts':'true'})
return payload;
}
async function getSegment(strava, accessToken, id)
{
const payload = await strava.segments.get({'access_token':accessToken,'id':id})
return payload
}
var tableau = []
var segId = []
const activities = getActivities(strava, accessToken)
activities.then(value => {
value.forEach((element, index) => {
const activity = getActivity(strava, accessToken, element['id'])
activity.then(value => {
value['segment_efforts'].forEach((element, index) => {
const segment = getSegment(strava, accessToken, element['segment']['id'])
segment.then(value => {
segId.push(value['id'])
})
//console.log(segId)
});
});
})
}) console.log(segId)
Regards
PS : Sorry for my english ...
Something like this should work. You need to always return the inner promises to include them in your promise chain. Consider splitting the code into functions to make it more readable.
getActivities(strava, accessToken).then(activities => {
return Promise.all(activities.map(elem => {
return getActivity(strava, accessToken, elem['id']).then(activity => {
return Promise.all(activity['segment_efforts'].map(elem => {
return getSegment(strava, accessToken, elem['segment']['id']).then(segment => {
segId.push(segment['id']);
});
}));
})
}));
})
.then(_ => {
console.log(segId);
});

async await mongodb find filter is not working, how can i make this work?

i want the code to not return everything except a specific value in the database objects that are returned but even after putting in the filter it still return the unwanted value. this is the code that i am using. The field that I am trying to remove from the objects is "ss". the filter function is written similar to what the MongoDB documentation has told me to do but i am missing something.
client side
this.blocks = await CatService.getAllBlocks()
static getAllBlocks() {
let result = new Promise ((resolve, reject) => {
axios
.get(`/api/cats/read-all`)
.then((res) => {
const data = res.data
console.log('RETURNED DATA:', data)
resolve(
data.map((block) => ({
...block,
createdAt: new Date(block.createdAt)
}))
)
})
.catch((err)=> { reject(err) })
})
return result
}
Server Code
router.get('/read-all', async (req, res) => {
const blocks = await loadBlocksCollection()
let retrievedData = await blocks.find(
{},
{ "ss": 0 }
).toArray()
res.send(retrievedData)
})
async function loadBlocksCollection() {
const uri = process.env.MONGO_URI
const db_name = process.env.DB || 'db_name'
const c_name = 'blocks'
const client = await mongodb.MongoClient.connect(
uri,
{
useNewUrlParser: true,
useUnifiedTopology: true
}
)
return client.db(db_name).collection(c_name)
}
this is not correct syntax:
let retrievedData = await blocks.find(
{},
{ "ss": 0 }
).toArray()
The correct way to filter Data is like this:
let retrievedData = await blocks.find(
{}
).project({ ss: 0 }).toArray()

Nothing executes after forEach loop in redux

My redux code looks like following
const getCategories = (res) => (dispatch,getState) => {
let categories = []
const result = res.results
console.log("here")
result.forEach((rawMaterial,index)=>{
if(!_.includes(categories,rawMaterial.category[0].name)){
categories.push(rawMaterial.category[0].name)
dispatch({type:UPDATE_CATEGORIES,categories})
}
});
console.log("here2")
}
Basically it is a function which pushes data into categories array from input parameter "res". What my problem is, "getCategories" function does not executes anything after forEach loop.
The below piece of chunk does not get executed in the above code.
console.log("here2")
Function executes nothing after forEach loop.
Full code
export const getRawMaterials = (params = {}) => (
dispatch,
getState,
{ fetch }
) => {
dispatch({ type: GET_RAW_MATERIALS_REQUEST, params });
const { token } = dispatch(getToken());
const { search, ordering } = getState().rawMaterials;
return fetch(
`/pands/raw-materials/?${qs.stringify({
search,
ordering
})}`,
{
method: "GET",
token,
success: res => {
const rawMaterials = res.results
dispatch({ type: GET_RAW_MATERIALS_SUCCESS, res });
const categories = getCategories(rawMaterials);
dispatch({type:UPDATE_CATEGORIES,categories})
},
failure: err => dispatch({ type: GET_RAW_MATERIALS_FAILURE })
}
);
};
//doubt here, function get return after forEach
const getCategories = (res) => {
let categories = []
const result = res;
return result.map(rawMaterial => {
if(_.includes(categories,rawMaterial.category[0].name)) return null;
return rawMaterial.category[0].name;
}
)
}
My code is not working from the following line:
const categories = getCategories(rawMaterials);
Neither it is giving an error. I think problem is with response coming from backend.
"res" is coming from backend. my data is in "res.results". When I type check "res.results" , it shows object but when I print it, it shows array of objects.

Categories