Send array from React to Express/Node to query Mongo - reactjs

I am having difficulty sending an array of Id numbers from React state, through Node/Express, then eventually to MongoDB.
The main difficulty is how to send an array from React to the server. Once I have it there in a usable array form, I believe I know how to query MongoDB using an array.
I have been trying to do this mainly with a GET method and have been unsuccessful using req.params or req.query. I have tried this with several versions of string templates, and using what the Axios docs say as well as many other answers on stack overflow, none have been successful.
I have also tried a couple of versions of a PUT request and also with no success.
The array of Ids already exists in props.completedJobs:
In React:
useEffect(() => {
const fetchData = async () => {
let completedJobsArray = props.completedJobs;
let json = JSON.stringify(data);
let getData = {jsonData: json};
const result = await axios('/api/brief/brief-array/' + getData);
setData(result.data);
};
fetchData();
}, []);
In Express app:
app.use("/brief", briefRouter);
then in the router:
router.get("/brief-array/:briefArray", briefController.brief_findArrayById);
then the function:
exports.brief_findArrayById = async (req, res) => {
try {
// console.log("req.body: ", req.body);
// console.log("req: ", req);
// console.log("req.query: ", req.query);
// console.log("req.params: ", JSON.stringify(req.params.briefArray));
const briefs = await GWJob.find({"_id": {$in: ["5d1297512c68bc49060bce7b", "5d1297092c68bc49060bce7a"] } });
res.json(briefs);
} catch (err) {
res.json({ message: err });
}
}
The MongoDB query works with the hard-coded IDs, but cannot get any of the above versions of console.logs to display any value other than undefined or "object object".
I am also unable to query the database using an array through Postman.
I expect to send 1 or more id numbers in an array to MongoDB, and receive those documents back to React on the front end. Any help with sending an array from state in React through Node/Express is much appreciated! Thank you.

You are sending Long string through the URL which is a limited length (~2047 char),
the safe away to accomplish what you are trying to do to use a post or put method and send the list in the body.
Example:
useEffect(() => {
const fetchData = async () => {
let completedJobsArray = props.completedJobs;
const result = await axios({
method: 'post',
url: '/brief-array',
data: completedJobsArray
});
setData(result.data);
};
fetchData();
}, []);

Another attempt, still returning undefined. If anyone has links to a thorough resource on sending/receiving data I would much appreciate it. I always have spent days trying to get one of these to work, until I started using Axios, but if I need to go beyond what I can do with Axios I am back to square one. Many thanks.
useEffect(() => {
const fetchData = async () => {
let completedJobsArray = props.completedJobs;
let queryData = {completedJobsArray};
const settings = {
method: 'POST',
headers: { 'Content-Type': 'application/json; charset=utf-8'},
body: JSON.stringify(queryData)
};
try {
const fetchResponse = await fetch(`/api/brief/brief-array`, settings);
const returnedData = await fetchResponse.json();
setData(returnedData);
} catch (error) {
console.error("error: ", error);
}
}
fetchData();
}, []);

Related

Handling axios errors in child components?

My React app has an api client component which handles axios calls to the back end. So, in the api component I have:
async function getData(path: string, params?: any) {
const object: AxiosRequestConfig = {
...obj,
method: 'GET',
headers: {
...obj.headers,
},
params,
};
const response: AxiosResponse = await axios.get(
`${baseUrl}${path}`,
object
);
return response;
});
}
(obj contains the headers and is defined earlier in the file; baseUrl is a constant which I import).
So, if I have a useEffect to retrieve data from the endpoint '/user/{userId}' whenever the state variable userId changes, I do this:
React.useEffect(() => {
const controller = new AbortController();
const getData = async () => {
try {
const url = `user/${userId}`;
let res = await Client.getData(url, {
signal: controller.signal,
});
... Do things with results ...
} catch (e) {
// Show error
if (!controller.signal.aborted) console.log('Error: ', e);
}
};
getData();
return () => {
controller.abort();
};
}, [state.userId]);
I'm just a bit confused about how errors will be handled in this code. So, if there's an error when the axios call is made (eg no network connection, the endpoint is wrong, or the user isn't found or whatever) will the catch block get called in the getData function? Or do I need a try...catch in the api component too?

Receiving undefined instead of JSON

I am using React axios to receive the JSON (array of objects) from server (server side is written on Go, I checked via Postman, JSON is sent properly).
Here is how I get the data on client side:
export const getPostData = async () => {
const URL = 'http://localhost:8083/test'
try {
const { data: { data }} = await axios.get(URL);
console.log(data)
return data;
} catch (error) {
console.log(error)
}
};
And this is how the getPostData is called in App.js:
const App = () => {
const [ posts, setPosts ] = useState([]);
useEffect(() => {
getPostData()
.then((data) => {
setPosts(data)
console.log(data)
})
},[]);
The problem is I get undefined in browser console. I found many similar questions asked here, but I could not find the solution (the Access-Control-Allow-Origin header is set when I send the JSON).
What should I learn more, where could be the problem? I would be very grateful for any help!
If this could be helpful, here is how I send the JSON in Go:
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Content-Type", "application/json")
c.JSON(http.StatusOK, gin.H{
"message": "Hello",
})
This looks suspect:
const { data: { data }} = await axios.get(URL);
That tries to read a property called data from an object on the data property of the response from axios, like this without the destructuring:
const data = (await axios.get(URL)).data.data;
Your Go code doesn't look like it puts a {"data": ___} wrapper around what it sends, and Axios only adds one layer of {data: ___} wrapper to what it gives you in the response, not two.
If you want the object from the JSON response, remove the inner destructuring:
const { data } = await axios.get(URL);
data will be {message: "Hello"} assuming the Go code sends the JSON {"message": "Hello"}.
Separately, your JavaScript code seems to expect an array of posts, but your Go code is just sending {"message": "Hello"}.

Parse data through API with useEffect for Algolia

I need to parse stock data through YahooFinance Stocks API, using RapidAPI here how it would look like as an example response.
https://rapidapi.com/integraatio/api/yahoofinance-stocks1/
The error I am getting is:
Uncaught (in promise) ReferenceError: Cannot access 'data' before
initialization
"results":1294 items
[100 items
0:{4 items
"exchangeCode":"NMS"
"symbol":"1"
"companyName":"1"
"industryOrCategory":"N/A"
}
1:{...}4 items
2:{4 items
"exchangeCode":"NMS"
"symbol":"AAON"
"companyName":"AAON, Inc."
"industryOrCategory":"Industrials"
}
3:{4 items
"exchangeCode":"NMS"
"symbol":"AAPL"
"companyName":"Apple Inc."
"industryOrCategory":"Technology"
}
]
useEffect(() => {
const config = {
headers: {
"x-rapidapi-host": "stock-market-data.p.rapidapi.com",
"x-rapidapi-key": APIKEY,
},
};
const fetchStocks = async () => {
const json = JSON.parse(data);
const results = Object.keys(json["results"]);
const stockInfo = results.map(
(result) =>
(result = {
result,
close: String(json["results"][result]),
})
);
const { data } = await axios.get(
"https://stock-market-data.p.rapidapi.com/market/exchange/nasdaq",
config
);
data.forEach((results) => {
results.objectID = results.length;
});
setStocks(data);
};
fetchStocks();
}, [])
This seems like a marshaling issue in your Javascript more than an Algolia issue. Perhaps because the await axios.get() needs to be in an async function per https://stackabuse.com/making-asynchronous-http-requests-in-javascript-with-axios/
To use the async/await syntax, we need to wrap the axios.get() function call within an async function
Curious the contents of data before and after the data.forEach() (also could that be a map?)

Code after fetch call is seemingly not read

I'm making a POST request to a local Express server with a JSON as its body and I'm doing this inside an async function:
async function sendSigninForm(login_form) {
const settings = {
method: 'POST',
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(login_form)
};
try {
await fetch(`http://localhost:9000/signin`, settings)
} catch (e) {
return e;
}
}
I tried accessing fetch's response in various ways, like
const fetchResponse = await fetch(`http://localhost:9000/signin`, settings);
const data = await fetchResponse.json();
or
const fetchResponse = await fetch(`http://localhost:9000/signin`, settings)
.then((value) => console.log(value));
but neither of these work, and I can't even print anything.
const fetchResponse = await fetch(`http://localhost:9000/signin`, settings)
.then(console.log("hello"));
This works, but of course it won't let me print fetch's response so it's useless to me. Also, the server is apparently working correctly as it gets the form as required and queries the database correctly.
Well, I forgot to send status codes server-side, and apparently that's the reason it hanged, although everything seemed to be fine and the queries were actually executed.

Map over Axios Response and append 2nd Axios Response to 1st

I'm creating a movie search React App using axios and the TMDb API. I want to make an axios.get call to search for a movie based on query, then map over the response, and pass the movie.id for each movie into a 2nd axios call to get more details for each movie.
I have tried many approaches, but I'm confused about how I need to return the values, or if I need to use async/await at some point in the code to wait for responses. I don't really know the best way to go about this - there's got to be a simple way to do it in sequence, but the asynchronous nature of the problem makes this difficult for me.
I thought it would be simpler to make my first axios call inside my search function, and then write a second function getDetails(id) that takes a movie id and makes an axios call with it.
Ideally, if I call the getDetails function in a map, get a response, append that response to my movies array, I could easily have an array of movies with appended details to each individual movie. I could then store the entire array in state, and use a single piece of state to manage all my movie data. I know that this process is not synchronous and that axios is promise based, but is there a way to go about this sequentially or am I approaching it all wrong? I know that .push() is not working in this situation, but I have tried other methods and they have always returned undefined, a promise, or an unintended result.
const search = query => {
axios
.get(
`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&language=en-US&query=${query}&page=1&include_adult=false`
)
.then(response => {
response.data.results.map(movie => {
const details = getDetails(movie.id);
return movie.push(details);
});
});
};
const getDetails = id => {
axios
.get(
`https://api.themoviedb.org/3/movie/${id}?api_key=${API_KEY}&language=en-US`
)
.then(response => {
return response.data;
});
};
EDIT 1: I've played with the answer by Abanoub Istfanous, and it seemed to return undefined with the added dispatch to update my state. I tweaked it more and the second set of code marked WORKING seems to be displaying some search results, but I cannot render any of the appended data from getDetails to the screen, as they are showing undefined. Am I still doing something incorrectly?
NOT WORKING:
const search = async query => {
dispatch({
type: MOVIE_SEARCH_REQUEST
});
const response = await axios.get(
`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&language=en-US&query=${query}&page=1&include_adult=false`
);
const data = Promise.all(
response.data.results.map(async movie => {
movie.details = await getDetails(movie.id);
})
);
dispatch({
type: MOVIE_SEARCH_COMPLETE,
payload: data
});
return data;
};
// getDetails is unchanged...
WORKING:
const search = async query => {
dispatch({
type: MOVIE_SEARCH_REQUEST
});
const response = await axios.get(
`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&language=en-US&query=${query}&page=1&include_adult=false`
);
const data = Promise.all(
response.data.results.map(async movie => {
movie.details = await getDetails(movie.id);
})
);
dispatch({
type: MOVIE_SEARCH_COMPLETE,
payload: response.data.results
});
return data;
};
// getDetails is unchanged...
I recommend to use Promise
/*
*#return data
*/
const getDetails = async (id) => {
const response = await axios
.get(
`https://api.themoviedb.org/3/movie/${id}?api_key=${API_KEY}&language=en-US`
)
return response.data
};
then
const search = async (query) => {
const response = await axios
.get(
`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&language=en-US&query=${query}&page=1&include_adult=false`
);
const data = Promise.all(response.data.results.map(async movie => {
movie.details = await getDetails(movie.id);
}))
return data
};
The reason why it returns undefined is because Promise.all() will be executed asynchronously on the job queue and will only return the result after all Javascript gets executed and the call stack gets empty. So a simple solution to your code will be placing an await before Promise.all().
const search = async query => {
dispatch({
type: MOVIE_SEARCH_REQUEST
});
const response = await axios.get(
`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&language=en-US&query=${query}&page=1&include_adult=false`
);
const data = await Promise.all(
response.data.results.map(async movie => {
movie.details = await getDetails(movie.id);
})
);
dispatch({
type: MOVIE_SEARCH_COMPLETE,
payload: response.data.results
});
return data; //No need to return data since you are dispatching it to redux store
};
You can read more about the asynchronous nature of JS on browsers here:
https://medium.com/#Rahulx1/understanding-event-loop-call-stack-event-job-queue-in-javascript-63dcd2c71ecd

Resources