How to display _id of the item on the screen in MongoDB? - node.js

I've entered _id by myself, but how can I display them in console?
Here is the router:
router.get('/getCompetitions', (request, response) => {
competitionTemplateCopy.find()
.then(data => response.json(data))
.catch(error => response.json(error))
});
Here is the function:
const onClickThat = (e) => {
e.preventDefault();
axios.get('http://localhost:4000/app/getCompetitions')
.then(response => console.log(response.data))
}
What I have:
I have only name but how can I display _id as well?

I think that you are getting an array of objects.Store all the data in another vaiable then try this.
const [data,setData] =useState([])
const onClickThat = (e) => {
e.preventDefault();
axios.get('http://localhost:4000/app/getCompetitions')
.then(response => setData(response)
}
data.map((e,i)=>{e._id})
I believe that this will work for you.

Related

Changing json object property from inside fetch using another fetch

I am creating a web application using the REST Countries API (link given)
https://restcountries.com/.
The API gives json with the property borders, which is an array of strings giving the cca3 of the bordering countries. I would like to get the names of the countries too and so am making another request for that data. So far this is what I have come up with. But, the json returned from the first request is never changed. I don't know what is going on, if anybody could advice?
const dataAPI = 'https://restcountries.com/v3.1/'
router.get('/country/:code', (req, res, next) => {
const code = req.params.code
fetch(`${dataAPI}/alpha/${code}?fields=borders`)
.then(response => response.json())
.then(json => fetch(`${dataAPI}/alpha?codes=${json.borders.join()}&fields=cca3,name`))
.then(response => response.json())
.then(bordersJson => {
fetch(`${dataAPI}/alpha/${code}`)
.then(response => response.json())
.then(data => {
data.borders = bordersJson
res.send(data)
}).catch(err => next(err))
}).catch(err => next(err))
})
Async/await is a better approach for this case.
const dataAPI = 'https://restcountries.com/v3.1/'
router.get('/country/:code', async (req, res, next) => {
try {
const code = req.params.code;
const borderJSON = await fetch(`${dataAPI}/alpha/${code}?fields=borders`).then(response => response.json());
// response: {"borders":["BGD","BTN","MMR","CHN","NPL","PAK"]}
const codes = borderJSON.borders.join(',');
const cca3 = await fetch(`${dataAPI}/alpha?codes=${codes}&fields=cca3,name`)).then(response => response.json());
// [{"name":{...},"cca3":"BGD"},{"name":{...},"cca3":"PAK"}]
res.send(cca3);
} catch (err) {
next(err);
}
});
The reason the borders property was not replaced was that the API endpoint used returns an array with one object, not the object itself. Also, I found that finding the borders separately was unnecessary.
Final Solution
router.get('/country/:code', (req, res, next) => {
const code = req.params.code
fetch(`${dataAPI}/alpha/${code}`)
.then(response => response.json())
.then(json => {
json = json[0]
fetch(`${dataAPI}/alpha?codes=${json.borders.join()}&fields=cca3,name`)
.then(response => response.json())
.then(bordersJSON => {
json.borders = bordersJSON
res.send(json)
}).catch(err => next(err))
}).catch(err => next(err))
})

Parse res.text Node express

I am retrieving a csv-file from a cloud based storage as res.text and need to convert it to json.
I am wondering if I should do the parsing in the return of fetchUrl or if I should do it in the route (res.send)?
const fetchUrl = async () => {
const URL_1 = 'https://file.csv'
const res = await fetch(URL_1)
return res.text()
}
router.get('/data', async (req, res, next) => {
try {
const getAllData = await fetchUrl();
console.log(getAllData, 'fetching?');
res.send(getAllData);
} catch (err) {
next(err);
//res.send({ message: err })
// res.status(404).send(err)
console.log(err)
}
})
I made use of a custom function, that converts it to json in the 2nd .then. So that I won't have to fetch upon every call to the endpoint since the data does not change.
Like so:
let getAllData
fetch('https://file.csv')
.then(res => res.text())
.then(data => {
getAllData = csvToJSON(data)
getAllData.forEach((item) => {
item.startTime = new Date(item.startTime)
})
})

Upload file (image) and data at the same time with react and node

I'm currently uploading an image and data using two calls to the server and I want to reduce this to one call to the server.
My current front end code as follows. The input field is :
<input
required
type="file"
onChange={this.fileUploaded}
/>
Uploading to state using this code:
userEvent.image = new FormData()
userEvent.image.append('file', e.target.files[0])
this.setState({ userEvent })
Sending to backend using this code:
let data = this.state.userEvent.image
let eventData = this.state.userEvent
eventData.token = localStorage.getItem("token")
axios.post(`${process.env.REACT_APP_API}/image`, data)
.then(res => {
axios.patch(`${process.env.REACT_APP_API}/events/${res.data._id}`, eventData)
.then(res => {console.log(res)})
.catch(err =>{console.log(err)})
})
.catch(err => {
console.log("imgerr", err)
})
and my backend code (for the first call to the server) is:
const Event = require('../models/events')
module.exports = (req, res) => {
let apiUrl = req.protocol + '://' + req.get('host') + '/'
let newEvent = {
imageURL: apiUrl + req.file.filename,
}
Event.create(newEvent)
.then(data => {
console.log('data', data)
res.send(data)
})
.catch(err => console.log(err))
}
When I try sending the image and the data in the one object, I get an error at the backend because req.file is undefined when I do this.
What am I missing?
I figured it out.
I appended the other data that I had saved in state that I also wanted to send to the backend.
Arrays of objects need to be stringified.
let userEvent = this.state.userEvent
console.log('userEvent.ticekts', userEvent.tickets);
let data = userEvent.image
data.append('title', userEvent.title)
data.append('venue', userEvent.venue)
data.append('description', userEvent.description)
data.append('startDetails', userEvent.startDetails)
data.append('endDetails', userEvent.endDetails)
data.append('token', localStorage.getItem("token"))
data.append('currency', userEvent.currency)
data.append('lat', userEvent.lat)
data.append('lng', userEvent.lng)
data.append('address1', userEvent.address1)
data.append('address2', userEvent.address2)
data.append('address3', userEvent.address3)
data.append('address4', userEvent.address4)
data.append('eventPassword', userEvent.eventPassword)
data.append('globalRefundPolicy', userEvent.globalRefundPolicy)
data.append('globalRefundOptions', JSON.stringify(userEvent.globalRefundOptions))
data.append('region', userEvent.region)
data.append('tickets', userEvent.tickets)
data.append('ticketTypesEquivalent', userEvent.ticketTypesEquivalent)
userEvent.tickets.forEach(item => {
data.append(`tickets[]`, JSON.stringify(item));
});
axios.post(`${process.env.REACT_APP_API}/image`, data)
.then(res => {
console.log('res', res)
})
.catch(err => {
console.log("imgerr", err)
})

Node Js Chaining the response

I am trying to make a db call insides a loop and want to add that data into one object and then send this object to user, but I am getting only a empty object at the user end.
I already checked this one asynchronous response into a loop in javascript NodeJS
router.get('/collection', (req, res) => {
const Data = {}
Section.find({})
.then(sections => {
sections.map(section => {
let id = section._id;
Product.find({section: id})
.then(products => {
// console.log(products)
Data[section.title] = {
title: section.title,
routeName: section.title,
id,
items: products
}
console.log(Data)
})
.catch(err => {
return res.status(500).json(err)
})
})
return res.json(data)
})
.catch(err => {
return res.status(500).json(err)
})
})
I want the output to be like :-
{
food : {
items: [...]
},
vegetable: {
items: [...]
}
}
food and vegetable are keys which will be obtained from a Database Call and items in each keys are returned from a seperate call to database.
return res.json(data) is executed before any of the mapped Product-promises are resolved (also there's a typo since you're returning data instead of Data). One way to do this is to map the find-promises and to use Promise.all on the mapped array. Something like:
router.get('/collection', (req, res) => {
const Data = {}
Section.find({})
.then(sections => {
const sectionProductPromises = sections.map(section => {
let id = section._id;
return Product.find({
section: id
})
.then(products => {
Data[section.title] = {
title: section.title,
routeName: section.title,
id,
items: products
}
});
});
return Promise.all(sectionProductPromises);
})
.then(() => {
res.json(Data)
})
.catch(err => {
res.status(500).json(err)
});
});

How to return the result of multiple promises in an express route?

Below is a route to retrieve information for my user's dashboard. I'm trying to avoid nesting all the promises (i hear its called callback hell) and still get the correct outcome. I'm assuming the code snippet below doesn't return anything because at run time it's blowing past all the promises and simply returning the three empty arrays before the promises have a chance to resolve. I tried to use async/await with no success and am not sure if that's even what I need. In this given situation what is the best practice for handling these promises and returning all the results all at once?
// #route GET api/user/dashboard
// #desc Retrieve all of the user's dashboard info
// #access Private
router.get("/dashboard",
passport.authenticate("jwt", { session: false }),
(req, res) => {
const donations = [];
const events = [];
const teams = [];
req.user.donations.forEach(donation_id => {
Donation.findOne({ _id: donation_id })
.then(donation => {
if (donation) {
donations.push(donation);
}
})
.catch(err => console.log(err));
});
req.user.events.forEach(event_id => {
Event.findOne({ _id: event_id })
.then(event => {
if (event) {
events.push(event);
}
})
.catch(err => console.log(err));
});
req.user.teams.forEach(team_id => {
Team.findOne({ _id: team_id })
.then(team => {
if (team) {
teams.push(team);
}
})
.catch(err => console.log(err));
});
return res.status(200).json({ data: { donations, events, teams } });
}
);
Use Promise.all to wait for all of the Promises to resolve first. You'll need to map each donation (and event, and team) to a Promise so you can call Promise.all on each group of Promises, and then you need to call Promise.all on those three groups.
Because the donations, events, and teams all need something similar (extract the _id property from an array, call .findOne with that _id, and filter all results), it would be good to abstract that all away into a single function, to keep your code DRY:
const getItems = async (arr, finder) => {
const foundItems = await Promise.all(
arr.map(({ _id }) => finder.findOne({ _id }))
);
return foundItems.filter(Boolean);
};
async (req, res) => {
const [donations, events, teams] = await Promise.all([
getItems(req.user.donations, Donation),
getItems(req.user.events, Event),
getItems(req.user.teams, Team),
])
.catch(err => console.log(err));
return res.status(200).json({ data: { donations, events, teams } });
};
Without async and await:
const getItems = (arr, finder) => (
Promise.all(
arr.map(({ _id }) => finder.findOne({ _id }))
)
.then((foundItems) => foundItems.filter(Boolean))
);
(req, res) => {
Promise.all([
getItems(req.user.donations, Donation),
getItems(req.user.events, Event),
getItems(req.user.teams, Team),
])
.then(([donations, events, teams]) => {
res.status(200).json({ data: { donations, events, teams } });
})
.catch(err => console.log(err));
};

Resources