60

I have stripe async code in my React app, and trying to add error handling in my code but have no idea how to handle it. i know how to do it with .then() but async/await is new to me

EDITED

added .catch() i got errors in network tab in response tab. but i can log it to console?

    submit = async () => {
    const { email, price, name, phone, city, street, country } = this.state;
    let { token } = await this.props.stripe
      .createToken({
        name,
        address_city: city,
        address_line1: street,
        address_country: country
      })
      .catch(err => {
        console.log(err.response.data);
      });

    const data = {
      token: token.id,
      email,
      price,
      name,
      phone,
      city,
      street,
      country
    };

    let response = await fetch("/charge/pay", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(data)
    }).catch(err => {
      console.log(err.response.data);
    });
    console.log(response);
    if (response.ok)
      this.setState({
        complete: true
      });
  };

thanks

starball
  • 20,030
  • 7
  • 43
  • 238
Exc
  • 1,473
  • 3
  • 15
  • 30
  • 1
    What errors do you want to handle? Please show us how you would have done it with `then`. – Bergi Jan 12 '19 at 21:22
  • @MattWay Rather the reverse - how to *catch* in `async`/`await` syntax. – Bergi Jan 12 '19 at 21:45
  • please take a look updated question – Exc Jan 12 '19 at 21:51
  • In the catch part, try console.log(error). Just a suggestion, try it out. – Avanthika Jan 12 '19 at 22:02
  • console.log at second .catch gives me in network tab and response tab i have {"name":"Name field is required","email":"Email is invalid","phone":"Phone field is required","city":"City field is required","street":"Street field is required","country":"Country field is required"} which is ok but how to access it?? to display on frontend? – Exc Jan 12 '19 at 22:09

9 Answers9

68

Fetch detects only network errors. Other errors (401, 400, 500) should be manually caught and rejected.

await fetch("/charge/pay", headers).then((response) => {
    if (response.status >= 400 && response.status < 600) {
      throw new Error("Bad response from server");
    }
    return response;
}).then((returnedResponse) => {
   // Your response to manipulate
   this.setState({
     complete: true
   });
}).catch((error) => {
  // Your error is here!
  console.log(error)
});

If you are not comfortable with this limitation of fetch, try using axios.

Yazan Rawashdeh
  • 1,017
  • 2
  • 16
  • 26
Avanthika
  • 3,984
  • 1
  • 12
  • 19
  • What do you mean by "*manually catched and rejected.*"? Please give an example. – Bergi Jan 12 '19 at 21:31
  • 3
    Fetch doesnt reject `response.status >= 400 && response.status < 600`. It considers them as exceptions, not errors. By rejecting manually, I meant the else part where we throw catch the exception and throw the error. – Avanthika Jan 12 '19 at 21:35
  • 3
    Sorry, I completely missed the `if (response.ok)` check in your code (which imo is better than comparing `response.status`). I must have mistaken the first `then` invocation for the options argument like in the snippets in the question and other answers. – Bergi Jan 12 '19 at 21:39
  • Thanks and true, that's even more cleaner approach. I just wanted to convey fetch's response handling with the code I wrote. – Avanthika Jan 12 '19 at 21:43
  • How do I get the errormessage returned from the server? – Rune Jeppesen Aug 06 '19 at 13:44
  • In catch, you can read the error messages from the server. If your server is returning you errors which you can see in network -> response tab, you can find it in: `error.response.data` – Avanthika Aug 06 '19 at 15:02
  • 3
    In given example both success and failure is handled using promise. What is the point of having `await` in the beginning? I don't change anything and can be safely removed. – ruX Jan 09 '21 at 00:03
  • @Avanthika do you have same solution for `async await` without using `.then()` – Ashish Kamble Nov 16 '21 at 07:07
  • Don't use both async/await and promise patterns (`.then`). For good `fetch` error handling practices, I recommend reading https://web.dev/fetch-api-error-handling/ – thdoan Jul 01 '23 at 09:50
25
var handleError = function (err) {
    console.warn(err);
    return new Response(JSON.stringify({
        code: 400,
        message: 'Stupid network Error'
    }));
};

var getPost = async function () {

    // Get the post data
    var post = await (fetch('https://jsonplaceholder.typicode.com/posts/5').catch(handleError));

    // Get the author
    var response = await (fetch('https://jsonplaceholder.typicode.com/users/' + post.userId).catch(handleError));

       if (response.ok) {
            return response.json();
        } else {
            return Promise.reject(response);
        }

};
Ishan Garg
  • 571
  • 5
  • 13
13

You can either use try/catch just like normal, imperative programming:

try {
    let response = await fetch("/charge/pay", {
      method: "POST",
      headers: {
          "Content-Type": "application/json"
      },
      body: JSON.stringify(data)
    });
} catch(error) {
    // Error handling here!
}

Or you can mix-and-match .catch() just like you do with promises:

let response = await fetch("/charge/pay", {
    method: "POST",
    headers: {
       "Content-Type": "application/json"
    },
    body: JSON.stringify(data)
}).catch(function(error) {
    // Error handling here!
});
LiraNuna
  • 64,916
  • 15
  • 117
  • 140
  • 17
    I'm pretty sure the first example will not catch the request error. – Valentin Genev Jul 18 '21 at 13:57
  • 3
    The first example does not catch errors. – Diego Fortes May 20 '22 at 23:13
  • 5
    The first example does catch the same error that would also be handled in the second example. But if the url is responding eg. with a 404 no error will be handled. This is because fetch is successful even if the returned response from the server is telling you that the resource is not found. – Andreas Riedmüller Sep 15 '22 at 08:30
4

This works if server returns { message: "some error" } but I'm trying to get it to support res.statusText too:

        const path = '/api/1/users/me';
        const opts = {};
        const headers = {};
        const body = JSON.stringify({});
        const token = localStorage.getItem('token');

        if (token) {
          headers.Authorization = `Bearer ${token}`;
        }

        try {
            const res = await fetch(path, {
                method: opts.method || 'GET',
                body,
                headers
            });

            if (res.ok) {
                return await (opts.raw ? res.text() : res.json());
            }

            const err = await res.json();

            throw new Error(err.message || err.statusText);
        } catch (err) {
            throw new Error(err);
        }
chovy
  • 72,281
  • 52
  • 227
  • 295
3
try {
  const response = await fetch('/api/getdata')
  if(response.ok) {
    // do sth if success
  } else {
    throw new Error(JSON.stringify({ code: response.status, message: response.statusText }))
  }
} catch (error) {
  alert(error)
}
yuxin wang
  • 31
  • 1
2

Wrap your await with try catch.

try {
    let response = await fetch("/charge/pay", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(data)
    });

    console.log(response);
} catch (error) {
    console.log(error);
}
Win
  • 5,498
  • 2
  • 15
  • 20
0
 async function loginWithRedirect(payload: {
        username: string;
        password: string;
    }) {
      const resp = await (await fetch(`${env.API_URL}/api/auth/login`, {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify(payload),
        credentials: "include",
      })).json();
      if (resp.error) {
        dispatch({type: "ERROR", payload: resp.error.message});
      } else {
        dispatch({type: "LOGIN", payload: resp});
      }
    }

sounish nath
  • 567
  • 4
  • 3
0

If response.ok is false you can throw an error then chain catch method after calling your function as follows

async function fetchData(){
    const response = await fetch("/charge/pay", {
      method: "POST",
      headers: {
          "Content-Type": "application/json"
      },
      body: JSON.stringify(data)
    });

    if(!response.ok){
        const message = `An error occured: ${response.status}`;
        throw new Error(message);
    }
    
    const data = await response.json();
    return data;
}

fetchData()
    .catch(err => console.log(err.message));
hkiame
  • 167
  • 3
  • 7
-1

I write promise function for using fetch in async await.

const promisyFetch = (url, options) =>
new Promise((resolve, reject) => {
fetch(url, options)
  .then((response) => response.text())
  .then((result) => resolve(result))
  .catch((error) => reject(error));
});

By the way i can use it easly in async with try catch

const foo = async()=>{
try {
    const result = await promisyFetch('url' requestOptions)
    console.log(result)
} catch (error) {
    console.log(error)
}
}

It was simple example, you could customize promisyFetch function and request options as you wish.

Gökhan Duman
  • 184
  • 2
  • 6