Sahand
Sahand

Reputation: 8370

How to handle empty json responses from server

I have a function in my front end app which calls my node.js backend server:

Client function:

  this.geocode = (placeName) => {
    const url = '/api/twitter/geocode?' + 'query=' + encodeURIComponent(placeName);
    return fetch(url)
      .then(processResponse)
      .catch(handleError)
  }


  // API Helper methods
  const processResponse = function (response) {
    if (response.ok) {
      console.log(response);
      return response.json()
    }
    throw response;
  }

  const handleError = function (error) {
    if (error.json) {
      error.json().then(error => {
        console.error('API Error:', error.message || error)
      })
    } else {
      console.error('API Error:', error.message || error)
    }
  }

Server route:

app.get('/api/twitter/geocode', (req, res) => {
  var parameters = {
    query: req.query.query
  }

  Twitter.get('geo/search', parameters)
    .then(response => {
      console.log("RESPONSE:")
      console.log(response);
      // check if there is a place for the given query
      if(response.date.places.length > 0){
       res.send(response.data.result.places[0].bounding_box.coordinates[0][0]);
      }
      res.send()
    })
    .catch(e => res.status(500).send('Something broke!')
    )
});

There is a problem when placeName is the name of a place that doesn't exist (or at least that Twitter doesn't know about). The console.log(response) in the backend shows me that such a request to the Twitter api leads to a return message without place data:

 { data: 
    { result: { places: [] },
      query: 
       { url: 'https://api.twitter.com/1.1/geo/search.json?query=FOOBARTOWN',
         type: 'search',
         params: [Object] } },
   resp: 

/*etc...*/

As you can see, places is an empty list. This response causes a crash in my frontend. I would like to know why. Look at the error message:

   const handleError = function (error) {
     if (error.json) {
  >     error.json().then(error => {
         console.error('API Error:', error.message || error)
       })
     } else {

And some other console outputs in the browser:

GET http://localhost:3000/api/twitter/geocode?query=FOOBARTOWN 500 (Internal Server Error) (model.js:197)
Uncaught (in promise) SyntaxError: Unexpected token S in JSON at position 0
    at handleError (model.js:214)

It seems like we are getting error 500. But why? My server hasn't failed. There's no error message in the node.js console.

Also, it seems like the program ends up in handleError, and then fails to convert the error to json.

  1. Why does it go to handleError? What's the error?
  2. Why does it say my server failed (error 500)?
  3. How can I make sure that if this.geocode gets an empty message, I don't crash, and return that empty message (so I can check for an empty message and act appropriately in my React.js component)?

Upvotes: 0

Views: 2150

Answers (1)

Marcos Casagrande
Marcos Casagrande

Reputation: 40444

Why does it go to handleError? What's the error?

Your server is sending a 500 status code, with Something broke! as response body.

An when you try to use res.json() on a non JSON string you get:

Uncaught SyntaxError: Unexpected token S in JSON at position 0

The line if(error.json) is not doing what you think it does. handleError is being called when response.ok is false, since you're throwing the fetch response object otherwise, in that case error argument will be a fetch response that implements Body, which has a json method, even if the body isn't a JSON, which is your case.

Your handleError can be written like this, where you will handle fetch errors and non 2xx responses.

const handleError = async function(error) {

  if(error instanceof Response) {

    if(error.headers.get('content-type').includes('application/json'))
      error = await error.json();
    else error = await error.text();

  }

  console.error('API Error:', error.message || error)
}

Why does it say my server failed (error 500)?

Place a console.log(e) on Twitter.get().catch and you'll find out.


Your Twitter.get().then is also wrong, since you're sending the response twice.

if(response.date.places.length > 0){
       res.send(response.data.result.places[0].bounding_box.coordinates[0][0]);
}
res.send()

Should be:

 if(response.date.places.length > 0)
   return res.send(response/*...*/);

 res.send()

Upvotes: 3

Related Questions