2

This is kind of a two part question, the first being specific to my personal circumstance and the second being an overall understanding of how things function.

I am performing a password reset for my application. The email gets sent along with a jwt attached to the URL. After the user clicks the URL they are taken to the reset password page that fires of an action with the jwt through componentWillMount function. This action then fires off the fetch:

static verifyResetPasswordToken(token) {
  const obj = JSON.stringify(token);
  return fetch('/api/auth/verifyResetPasswordToken', {
    method: 'POST',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
    credentials: 'include',
    body: obj
  })
    .then(res => {
      console.log('THEN');
    })
    .catch(error => {
      console.log('CATCH');
    });
}

On the API I'm handling this by taking the token passed in the body and checking if it has expired or not:

export function verifyResetPasswordToken(req, res, next) {
  const token = jwt.decode(req.body.token);
  if (token.exp > Date.now() / 1000) {
    res.status(200).json();
  } else {
    res.status(401).json();
  }
}

NOTE I understand this is not a secure way to check the validity of a token. I just need to figure out if it is expired or not.

Here is where the confusion lies. When the 401 status gets returned, my promise is rejected. It is my understanding that fetch does not handle errors this way. That the only thing fetch catches is network connectivity and that I should still hit my then() block even with 400 & 500 http status errors. Any idea as to why my promise is getting rejected with a 401 status? Why am I landing in the catch block? How do I avoid this from happening? How do I handle different status that I want to respond with on my server?

My second question revolves around all of this. What is the best practice for handling server errors when working with fetch and maybe specifically React Redux? This is my first time using fetch and any light that can be shed to understanding how I should handle server side errors would be greatly appreciated.

static verifyResetPasswordToken(token) {
  const obj = JSON.stringify(token);
  return fetch('/api/auth/verifyResetPasswordToken', {
    method: 'POST',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
    credentials: 'include',
    body: obj
  })
    .then(res => {
      if(res.ok) {
        console.log('THEN');
      } else {
        console.log('ELSE');
      }
    })
    .catch(error => {
      console.log('CATCH');
    });
}
Nappstir
  • 995
  • 2
  • 20
  • 38

1 Answers1

1

As per MDN, and as you already note, the fetch() API only rejects a promise when a “network error is encountered, although this usually means permissions issues or similar.”

However fetch provides an ok flag that indicates whether an HTTP response’s status code is in the successful range or not.

static verifyResetPasswordToken(token) {
  const obj = JSON.stringify(token);
  return fetch('/api/auth/verifyResetPasswordToken', {
    method: 'POST',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
    credentials: 'include',
    body: obj
  }).then(res => {
     if (!res.ok) {
       throw Error(res.statusText);
     }
     return res;
  })
    .then(res => {
      console.log('THEN');
    })
    .catch(error => {
      console.log('CATCH');
    });
}
Paul Fitzgerald
  • 11,770
  • 4
  • 42
  • 54
  • I was using the `res.ok` function (see example above). Yet my promise still got rejected. It appears that the difference is you have thrown in a callback to your callback. Is this normally how you need to handle responses? Any idea as to why the new example above doesn't work? thanks for your time Pual. – Nappstir Aug 15 '17 at 20:53
  • if the error will not be rejected by the `fetch` api then you need to reject it yourself by throwing an error. This is probably a shortcoming with the API but this is the suggested workaround. So as noted above as the fetch() API only rejects a promise when a network error is encountered you need to throw the error yourself if it falls outside this category. You could possibly look at using a library that will prob make it easier to handle these such as axios which was at least previously, the library react recommended using for ajax requests - https://github.com/mzabriskie/axios – Paul Fitzgerald Aug 15 '17 at 20:55
  • What is interesting is I never hit the `then` block. Even if I throw an error myself. I never hit it. Which leads me to believe that the `fetch` API is handling the `401` status as an error. =/ – Nappstir Aug 15 '17 at 20:59
  • I'm a little embarrassed, but I wasn't actually testing this with an expired token. Now that I am using the example you have provided(copy pasted), it is still rejecting the promise and I'm landing in the `catch`. Any ideas? – Nappstir Aug 15 '17 at 21:11
  • @Nappstir no need to be embarrassed :-) I thought you wanted to land in the `catch` block. – Paul Fitzgerald Aug 15 '17 at 21:12
  • So now knowing that I am trying to avoid the `catch` block, any ideas as to why responding with a `401` would reject the promise? – Nappstir Aug 15 '17 at 21:36
  • @Nappstir when using your code, and not using the code I provided? Also, are you having any CORS issues? Also, this could be worth a read https://github.com/github/fetch/issues/201 – Paul Fitzgerald Aug 15 '17 at 21:41
  • The promise still gets rejected with the code that you provided. Never hit the `then` block. It will go straight to rejecting the promise. The error the server returns is as follows: `Failed to load resource: the server responded with a status of 401 (Unauthorized)`. This should be CORS related I dont think. I have looked into it and it doesn't seem to be the problem. I was reading through that exact link making sure that CORS wasn't an issue. – Nappstir Aug 15 '17 at 21:55