75

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

5
  • 1
    What errors do you want to handle? Please show us how you would have done it with then. Commented Jan 12, 2019 at 21:22
  • @MattWay Rather the reverse - how to catch in async/await syntax. Commented Jan 12, 2019 at 21:45
  • please take a look updated question Commented Jan 12, 2019 at 21:51
  • In the catch part, try console.log(error). Just a suggestion, try it out. Commented Jan 12, 2019 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? Commented Jan 12, 2019 at 22:09

9 Answers 9

79

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.

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

10 Comments

What do you mean by "manually catched and rejected."? Please give an example.
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.
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.
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.
Don't use both async/await and promise patterns (.then). For good fetch error handling practices, I recommend reading web.dev/fetch-api-error-handling
|
30
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);
        }

};

Comments

14

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!
});

3 Comments

I'm pretty sure the first example will not catch the request error.
The first example does not catch errors.
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.
8
try {
  const response = await fetch('/api/getdata')
  if(response.ok && response.status == 200) {
    // do sth if success
  } else {
    throw new Error(JSON.stringify({ code: response.status, message: response.statusText }))
  }
} catch (error) {
  alert(error)
}

This will catch the same errors that a Promise.catch() would, i.e. a fail to connect.

However, HTTP errors (400... 500...) will not be caught.

These must be handled by checking response.status

2 Comments

Welcome to stackoverflow, it is always best to explain your answer.
You could avoid the else altogether if you just checked for !response.ok and response.status !== 200 before executing the rest of the code.
5

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);
        }

7 Comments

This works & is clear.
Thanks. Its the best I've come up with so far. Add some more.
This does not work for NetworkError errors. You'll see error logs in the console complaining about uncaught errors.
Best answer found so far, thanks!
Should be checking res.status too so that HTTP errors are handled as well as a fail (network connection fail is err)
|
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);
}

3 Comments

still can't get error on frontend... my object with errors is present in network tab...
This won't work. Tried this, and still saw uncaught errors.
Your uncaught errors are HTTP errors which you catch with response.status != 200
2

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));

Comments

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});
      }
    }

Comments

-2

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.

1 Comment

Avoid mixing await and promises... Pick one.

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.