0

I need to request data from my REST server to populate my UI (frontend). In doing so, I need to request some data from my and other servers. One such request is to get a list of states (provinces), process each one and add them to a select HTML component. I use fetch() and .json() amongst other tools to do this.

Problem:

In calling my REST server for json data, I receive the following data (taken from Chrome console):

{provinces:[Eastern Cape,Mpumalanga,Western Cape,Gauteng,KwaZulu Natal,North West,Northern Cape,Free 
State,Limpopo]}

I intend to add each of these as an option to a select. While attempting to acquire the value for the provinces key, I get undefined.

I am making this call using:

fetch("http://localhost:3443/app/location/provinces").then(e => e.json()).then(e => console.log(e.provinces));

Further, since I can directly refer to json keys using the [] operator, I attempt this using

fetch("http://localhost:3443/app/location/provinces").then(e => e.json()).then(e => console.log(e['provinces']));

which as you may have guessed aswel, also returns undefined.

For the record, the full Chrome Console output is

Promise {<pending>}
undefined

Looking over some SO examples, I believe my call(s) may be correct, this one, and this one, and this one all which confirm its validity.

What else have I tried:

This SO post and this one suggested to use the json data response inside of the same then() call e.g.

fetch("http://localhost:3443/app/location/provinces").then(e => {
    e.json().then(s => {
        console.log(s['provinces']);
 });
});

and

fetch("http://localhost:3443/app/location/provinces").then(e => {
    e.json().then(s => {
        console.log(s.provinces);
 });
});

both which return:

Promise {<pending>}
undefined

What am I missing / doing wrong?


Update

Screenshot of Chrome console in order of commands listed above:

enter image description here

Resource file za-province-city.json

NodeJS express code:

const express = require('express');
const router = express.Router();
const fs = require('fs');
const raw = fs.readFileSync("./res/za-province-city.json");
const map = JSON.parse(raw);
const mapProvinceCity = {};
map.forEach(item => {
    if (!mapProvinceCity.hasOwnProperty(item.ProvinceName)) {
        mapProvinceCity[item.ProvinceName] = [];
    }
    mapProvinceCity[item.ProvinceName].push(item.City);
});
for (let key in mapProvinceCity) {
    mapProvinceCity[key].sort((a, b) => a.toLocaleString().localeCompare(b.toLowerCase()));
}

router.get('/location/provinces', function (req, res, next) {
    let strings = Object.keys(mapProvinceCity);
    let json = JSON.stringify({provinces: strings}).replace(/"/g, '');
    return res.json(json);
});

router.get('/location/:province/cities', function (req, res, next) {
    let province = req.param('province');
    let cities = mapProvinceCity[province];
    let json = JSON.stringify({cities: cities}).replace(/"/g, '');
    return res.json(json);
});

module.exports = router;

Note: if you are wondering about the replace(), each time I requested data in postman, I got

enter image description here

10
  • "I receive the following data (taken from Chrome console)" 👈 where? What particular console.log() line produced that result? Commented Aug 17, 2020 at 0:53
  • 2
    Is the data you're getting back exactly as you've included here? Because that's not valid json. (No quotes.) If that's what's being returned from the server, the e.json() call is probably throwing. Commented Aug 17, 2020 at 0:53
  • @Phil adding screenshots, give me a sec Commented Aug 17, 2020 at 0:54
  • @rayhatfield see above comment Commented Aug 17, 2020 at 0:55
  • 1
    What on Earth are you doing with that string replace? Also, you're double-encoding your mangled JSON so in the end, it will just be an unusable string. Just use res.json({provinces: strings}) Commented Aug 17, 2020 at 1:07

1 Answer 1

3

I think your issues all stem from a misunderstanding of Express' res.json().

This is basically a shortcut for

res.set("Content-type: application/json")
res.status(200).send(JSON.stringify(data))

I imagine your problems started when you thought you needed to stringify your data. What happens then is that your data is double-encoded / double stringified, hence the extra quotes. Removing the quotes though mangles your data.

console.log() is not a particularly good debugging tool as it obfuscates a lot of information. In your code, s is actually a string

"{provinces:[Eastern Cape,Mpumalanga,...]}"

I suggest you use the actual debugger instead.


The simple solution is to use res.json() as intended

router.get('/location/provinces', function (req, res, next) {
  return res.json({ provinces: Object.keys(mapProvinceCity) });
});

with your client-side code looking like

fetch("http://localhost:3443/app/location/provinces")
  .then(res => {
    if (!res.ok) {
      throw res
    }
    return res.json()
  })
  .then(data => {
    console.log('Provinces:', data.provinces)
  })

This goes for all your Express routes. Do not use JSON.stringify().

Sign up to request clarification or add additional context in comments.

1 Comment

You are correct about the res.json() being a shortcut for the header, etc. I recall going through that not to long ago. Hopefully it all comes back to me pretty soon :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.