Changing json object property from inside fetch using another fetch - node.js

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))
})

Related

How to display _id of the item on the screen in MongoDB?

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.

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)
})
})

Problem regarding the node-fetch api. Specifically, when attempting to use fetch to retrieve JSON data

Code below retrieves the JSON data without any problems.
app.get('/startgame', (req, res) => {
res.send('Welcome To The Start Of The Game')
fetch("https://deckofcardsapi.com/api/deck/new/shuffle/deck_count=1")
.then(res => res.json())
.then(json => console.log(json))
})
Code below returns undefined when I do console.log(json). The only difference between these two blocks of code are the squiggly brackets on the bottom block of code. I would like to know why exactly is this happening. My understanding is that they should both produce the same result.
app.get('/startgame', (req, res) => {
res.send('Welcome To The Start Of The Game')
fetch("https://deckofcardsapi.com/api/deck/new/shuffle/?deck_count=1")
.then((res) => {res.json()})
.then((json) => {console.log(json)})
})
The reason is that this syntax:
() => foo()
Is short for:
() => { return foo(); }
On this line in your code:
.then((res) => { res.json() })
you are no longer returning the result of res.json(). You need to explicitly use return when you use curly braces:
.then((res) => { return res.json(); })
Which is equivalent to:
.then((res) => res.json())

Express : unable to load the intended path with middlewares

i am beginer with Express and I have a rather strange functionality that I am achieving with the middlewares. Here I call a URL which is fetched by its middleware and then on next() another middleware gets called. now in the next() of the second middleware I need to load the component, but issue is that, URL is not changing after the first middleware's next().
Code :
Express App : Router :
app.use('/common/global/login', mainHandler);
app.use('/common/*', subhandler, SuccessComponent);
Middleware :
export function mainHandler(req, res, next) {
const global-url= "someURL"
if (global-url) {
return fetch(global-url)
.then((response) => response.json())
.then((response) => {
if (response.data) {
next();
} else {
throw Error(response.statusText);
}
})
.catch((error) => {
res.redirect('/session-expired');
next(error);
});
}
res.redirect('/session-expired');
}
export function subhandler (req, res, next) {
const other_url= "someOtherURL"
return fetch(other_url)
.then((response) => response.json())
.then((response) => {
if (response.data) {
// here it not loading the SUCCESSCOMPONENT as the URL still remains /common/global/login
return next();
}
throw Error(response.statusText);
})
.catch((error) => {
next(error);
res.redirect('/session-expired');
});
}
res.redirect('/session-expired');
}
You have a syntax error with your code, it may be worth fixing this first to see if it is contributing to the error you are having:
export function mainHandler(req, res, next) {
const global-url= "someURL"
if (global-url) {
return fetch(global-url)
...
You cannot define a variable that contains a hyphen -, as this read as the subtract operator.
const global-url = ... , should be const global_url = ...
And of course update all instances where you are calling this variable.
In your code's current state, next() is not being called by the first middleware because if (global-url) {...} would not return a thruthy value therefore not triggering the next middleware in the chain.
Try:
export function mainHandler(req, res, next) {
const global_url= "someURL"
if (global_url) {
return fetch(global_url)
.then((response) => response.json())
.then((response) => {
if (response.data) {
next();
} else {
throw Error(response.statusText);
}
})
.catch((error) => {
res.redirect('/session-expired');
next(error);
});
}
res.redirect('/session-expired');
// Note that if this 'if' is not satisfied, 'next()' is not called.
}

POST request for many to many in express

I have 2 tables: user and material which have a m:m relationship. The intersection entity is journalMaterials. I am trying to send a POST request to insert into journalMaterials. Also, this table has 2 attributes: recycledQuantity and recycleDate. I tried something, but if i insert with a materialId which doesn't exist it doesn't give me "not found".
app.post('/users/:uid/materials/:mid', (req, res, next) => {
User.findById(req.params.uid)
.then((user) => {
if (user){
let journalMaterial = req.body
journalMaterial.userId = user.id
Material.findById(req.params.mid)
.then((material) => {
if (material){
journalMaterial.materialId = material.id
return JournalMaterial.create(journalMaterial)
}
else{
res.status(404).send('not found')
}
})}
else{
res.status(404).send('not found')
}
})
.then(() => {
if (!res.headers){
res.status(201).json('created')
}
})
.catch((err) => next(err))
})
I've solved it. Here is the correct code.
app.post('/users/:uid/materials/:mid', (req, res, next) => {
const { uid, mid } = req.params;
Promise.all([
User.findById(uid),
Material.findById(mid)
])
.then(([user, material]) => {
if (user && material) {
let journalMaterial = req.body
journalMaterial.userId = user.id
journalMaterial.materialId = material.id
res.status(201).json('created')
return JournalMaterial.create(journalMaterial)
}
res.status(404).send('not found')
})
.catch(err => next(err));
})
Slightly re-wrote this to make it a bit more readable. Removed your nested promise calls... (let's not dive into promise hell when they try to get rid of callback hell..)
app.post('/users/:uid/materials/:mid', (req, res, next) => {
const { journalMaterial } = req.body;
const { uid, mid } = req.params;
Promise.all([
User.findById(uid),
Material.findById(mid)
])
.then(([user, material]) => {
if (user && material) {
journalMaterial.userId = user.id;
journalMaterial.materialId = material.id;
return JournalMaterial.create(journalMaterial);
}
res.status(404).send('not found');
})
.then(() => {
if (!res.headers) {
res.status(201).json('created');
}
})
.catch(err => next(err));
});
Your check against if(user) currently passes. It seems that if that's what is happening, you're always getting an object back. Lots of databases generally don't simply return a null or false value, but rather an object with a bunch of meta data. In that object is generally the data you requested (ie, user.data.id, but it may be that user.data is NULL). Can you verify what the exact contents of Users is? It's evaluating to truthy, thus it must have something in it.

Resources