5

I'm trying to use the Javascript fetch method, however, it does not seem to work asynchronously.

Here's my code:

fetch(`${global.URL}${url}`, requestConfig)
  .then(res => res.json())
  .then(res => {
    console.log('response', res);
    return res;
  })
  .catch(error => {
    console.log('error: ', error)
  })

I get the following error 70% of the time, then the other 30%, a valid response is received, when I save the file and it re-renders, it sometimes works.

error:  SyntaxError: Unexpected token T in JSON at position 0
    at parse (<anonymous>)
    at tryCallOne (core.js:37)
    at core.js:123
    at JSTimers.js:277
    at _callTimer (JSTimers.js:135)
    at _callImmediatesPass (JSTimers.js:183)
    at Object.callImmediates (JSTimers.js:446)
    at MessageQueue.__callImmediates (MessageQueue.js:396)
    at MessageQueue.js:144
    at MessageQueue.__guard (MessageQueue.js:373)

I've tried calling it inside and async/await function but it does not help.

EDIT 1:

this is how I make my requests

const authenticityToken = global.TOKEN

const query = (url, config) => {
  const requestConfig = {
    credentials: 'same-origin',
    ...config,
    headers: {
      'X-Requested-With': 'XMLHttpRequest',
      'Content-Type': 'application/json',
      Accept: 'application/json',
      Authorization: authenticityToken,
    },
  }

  return fetch(`${global.URL}${url}`, requestConfig)
  .then(res => res.json())
  .then(res => {
    console.log('response', res);
    return res;
  })
  .catch(error => {
    console.log('error: ', error)
  })
  // .then(handleResponseError)
}

export const get = (url, data) =>
  query(data ? `${url}?${stringify(data)}` : url)

export function fetchUser() {
  return (
    get('/api/v3/me/')
  )
}

Then I call the function inside my component like so:

  const fetchUserAction = () => {
    fetchUser()
    .then((response) => {
      if(response) setUser(response.data)
    })
  }

  useEffect(() => {
    fetchUserAction()
  }, [])
10
  • 3
    wat is the response in the network panel ? each time it errors out.. do u get valid json in the response ?
    – Panther
    Commented Aug 12, 2020 at 13:42
  • there is no request in the network tab
    – Hugo
    Commented Aug 12, 2020 at 13:44
  • 3
    log here .then(res => { console.log({res}); return res.json()}) Commented Aug 12, 2020 at 13:50
  • 2
    Well that certainly doesn't look like JSON. Your problem is at the server.
    – Pointy
    Commented Aug 12, 2020 at 13:55
  • 2
    hmm.. looks like you are getting 401 which means it is unauthorized. So you would not get a valid response. Fix why it is providing you a 401 randomly and move on..
    – Panther
    Commented Aug 12, 2020 at 13:55

3 Answers 3

9

This type of error usually happens when your server returns something which is not JSON. In my experience, 99% of the time the server is returning a generic error message. Often times servers will have a generic "catch all" error handler which returns something like:

There was an error processing your request.

In this case, if you tried to use JSON.parse (or res.json() in your case), you would get the error you are experiencing. To see this, paste this into your console:

JSON.parse("There was an error processing your request.")
//-> Uncaught SyntaxError: Unexpected token T in JSON at position 0

Solution 1: Usually the server will set a proper status code whenever there is an error. Check to make sure the response status is 200 before parsing:

fetch('...').then(res => {
  if (res.status !== 200) {
    throw new Error(`There was an error with status code ${res.status}`)
  }
  return res.json()
)

Solution 2: Update your server code to return an error message in JSON format. If you're using node and express, this would look something like this:

function errorHandler (err, req, res, next) {
  if (res.headersSent) return next(err)

  const message = 'There was an error processing your request.'
  res.status(500)
  if (req.accepts('json')) {
    // The request contains the "Accept" header with the value "application/json"
    res.send({ error: message });
    return;
  }
  res.send(message);
}

Then, you would update your frontend code accordingly:

fetch('...')
.then(res => res.json())
.then(res => {
  if (res.error) {
    throw new Error(res.error)
  }
  return res
)
5

This kind of error Unexpected token T in JSON at position 0 always happens when the string you are trying to parse cannot be parsed as JSON. This specific error means that the string starts with the character 'T' and not with a '{' as strings that can be parsed to JSON should start. There's a very strict format that allows your string to become an object. This is probably not the problem if you made sure on the backend that your code takes an object, stringifies it, and sends the text. If you know that on the backend the only thing that can be sent is a stringified object, there is probably nothing wrong there.

The second more plausible answer is that your request failed, I see you prepared a catch block in case the request returns an error, but there's a problem there. The request could have failed for several reasons, if you say it happens only some of the time it is probably CORS problems or a logical bug on the backend. In that case, you would like to see the response itself and not an already parsed response. What essentially happens is that when your request succeeds the body is successfully parsed to an object and everything works fine, but when the request fails, the response would be an exception that starts with a T, for example, a TimeoutException that when you try to parse it fails because it starts with a T and not as JSON. What you need to see is the response before it is parsed to JSON, and only if it is not an error, you should try to parse it.

The problem in your code is that the first thing you do is to try and parse it as JSON. I would suggest you comment out this line and simply print, either the successful request or the failed request as strings. I'm pretty sure you will find that in 70% of the time, you will see the JSON string that you expected and in the remaining 30, you will get an exception string (that might be even thrown automatically by your backend hosting service, like Timeout exceptions, they might not be treated as errors but as strings. This, unfortunately, happens a lot on the free plan of Firebase functions where the time a function is running is limited to a certain number of seconds, you should check it in the plans' description on their website) that starts with a T. This will most certainly help you find where the problem is by giving you more information.

On another note, I warmly recommend you to stop using then and catch and instead start using the far superior async/await syntax that helps you keep your code simple and organized. If it's compatible with all the engines you are targeting, read the Mozilla documentation about it, it's pretty straightforward: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

Have a nice day and happy coding

0

Try this method

const response = await fetch('https://apitest.authorize.net/xml/v1/request.api', {
    method: 'post',
    body: JSON.stringify(jsonBody),
    headers: new Headers({'content-type': 'application/json',Accept: 'application/json',}),
    
});

const text = await response.text();
console.log(text);
   

return new Response(text, { status: 200, statusText: 'OK', headers: { 'Content-Type': 'application/json' } });

Not the answer you're looking for? Browse other questions tagged or ask your own question.