I am trying to use a proper way to handling my errors in my React app with Axios.
When the connection is dead, I won't get a status code, so I can't check the status code of the response. What is the elegant way to handle errors without a status code?
try {
const res = await axios.get("/login");
} catch (error) {
const {status} = error.response;
if (status === 401) doSomething();
}
I get this error:
[Unhandled promise rejection: TypeError: undefined is not an object
Yes, you are correct. Unhandled promise rejection and uncaughtExceptions are not the same. To handle promise rejection you will need to check for "res" value like so:
try {
const res = await axios.get("/login");
if(res.err) {
// An error exist
// Do handle response error
}
} catch (error) {
const {status} = error.response;
if (status === 401) doSomething();
}
The reason we are checking in the response value is because we are using await. When we use await we will get either the resolved response or the rejected response. It will not throw an exception.
throw Error("This is an exception")
This will be caught when using a wrapper around the code like a try-catch.
Another way of solving this issue is by adding a .catch after the fetch. Like so:
try {
const res = await axios.get("/login").catch(err => console.error(err));
} catch (error) {
const {status} = error.response;
if (status === 401) doSomething();
}
Now if the fetch failed, res will be undefined, because we only returned what console.error returned which is undefined.
Related
Is there a way to mock what a thrown exception in the try catch block? I want to test my function that the url string in error.message is indeed replaced with replacedURL string. How can I easily test it or mock the thrown exception to an object with this URL.
async function getUsers() {
try {
const response = await fetch('/api/users');
if (response.status >= 400) {
console.error('Could not fetch users');
return;
}
const users = response.json();
return users;
} catch (error) {
let message = error.message;
if (message) {
message = message.replace(/https:\/\/some.url.com /g, 'replacedURL');
delete error.message;
}
console.error('error', error);
}
}
Here's my test
test('url in error message is replaced', () => {
expect(getUsers()).toThrow('replacedURL');
})
You might want to actually throw an error in your function for it to be caught by toThrow.
Also, I'd recommend that your test doesn't actually tries fetching from the API (unless you're sure that's what you want it to do). Instead, consider using a custom caller (which can extend fetch) which you can then mock the response for, like const caller = jest.fn().mockRejectedValue({ message: 'https:://some.url.com' });
I'm making an API request using the async/await pattern in a try/catch block..
async myRequest(data) {
try {
await api.post('/my-endpoint/', data).then((response) => {
console.log(response.data)
});
} catch (ex) {
// WANT TO READ RESPONSE DATA HERE
}
}
If the request succeeds without errors, I am able to read the response with the .then() method.
If the request fails, this API returns a 422 code that triggers the try/catch exception.
However, if the request fails, this API still returns some data in the response body that I would like to to read but unable to because catch is triggered and .then() is never run.
How can I get the response body from the async function within the catch block?
Try this if you are on Axios lib:
catch (e) {
console.log(e.response.data);
}
console.log uses the toString method to format Error objects so it is hard to find the response property if you only log e (error).
Docs: https://axios-http.com/docs/handling_errors
The error object is stored in ex so you can log the error with
} catch (ex) { console.log(ex) }
One of the problems you are making is putting the .then after you call your API. When you are using the try-catch block you don't need to do the then because you can save the result into a variable.
One of the benefits of the try-catch block is that the errors are handled and you can do multiple async calls
async myRequest(data) {
try {
const response = await api.post('/my-endpoint/', data)
console.log(response.data)
} catch (ex) {
console.log(ex)
}
}
If you want to retrieve the response data from a catch block, you can do it technically.
See the following code.
const myRequest = async () => {
try {
const data = await fetch('https://jsonplaceholder.typicode.com/xxx/1').then((result)=> {
// You can change the status code to 422
if (result.status === 404) {
throw new Error('You can put your data here.')
// Make sure the argument you passed in Error is a string
}
return result.json()
})
} catch(e){
console.log(e.message);
}
}
myRequest()
This is not any conceptual question. just want to correct my logical side.
Case 1:
try {
var to = await new IamErrorAlways()
if (to && to instanceof Error) return to // is this correct way to handle.
} catch (error) {
// report failure.
return error
}
Case 2:
try {
var to = await new IamErrorAlways()
if (!to) throw new error('Expected to to return error') // or is this correct way to handle.
} catch (error) {
// report failure.
return error // <---- catch will return awaited error
}
Out of both which one is good.
Whenever promise rejects, it will not be returned as a value it will be thrown. And only way to check for thrown error is to catch it.
This is a typical example of a promise being rejected:
const promise = function() {
return Promise.reject('hello');
};
(async () => {
try {
const promiseVal = await promise();
console.log(promiseVal);
} catch (err) {
console.log(err+' from error');
}
})();
So in this case the console.log in try block won't even execute. The catch will be executed printing hello from error.
Running very simple test where I'm passing invalid credentials within the basic authorization header and I'm expecting the server to return 401
const request = require('request-promise');
const expect = require('chai').expect;
const basicToken = require('basic-auth-token');
describe('PUT Endpoint', function () {
it.only('should return unauthorized if basic token is incorrect', async function (done) {
let options = {
url: `http://url_to_handle_request`,
resolveWithFullResponse: true,
headers: {
Authorization: `Basic ${basicToken('INVALID', 'CREDENTIALS')}`
}
};
try {
await request.put(options); // this should throw exception
} catch (err) {
expect(err.statusCode).to.be.equal(401); // this is called
}
done();
});
});
Problem with this code is that the expect clause resolves to false (because the server responded e.g. with 403) and the test ends up with error:
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): AssertionError: expected 403 to equal 401
If I omit the done callback, the test just hangs (the name is in red) and is apparently "waiting" for something to finish
I know it will work if I would rewrite it to use standard promises approach. I'm just curious how to do it via async/await.
Thanks
Remove done from parameter (and as well from function body), then Mocha will expect that you return Promise.
Async functions returns promises by default.
If you dont throw error, it returns resolved promise.
Change this code block
try {
await request.put(options); // this should throw exception
} catch (err) {
expect(err.statusCode).to.be.equal(401); // this is called
}
done();
to
await request.put(options)
.then(()=>{ })
.catch(function (err)){
expect(err.statusCode).to.be.equal(401);
}
I have a simple async function. It just sends a request and returns the data:
export const updatePanorama = async ({ commit }, payload) => {
const urlEnd = '/v1/pano/update'
const type = 'post'
const resp = await api.asyncRequest(urlEnd, type, payload)
commit('SET_PANORAMA', resp.data)
return resp
}
And this is how I'm using the function:
handleUpdatePanorama (panorama) {
const payload = {}
this.updatePanorama(payload).then(resp => {
this.setIsLoading(false)
this.handleAlert('updateSuccess', 'success')
}).catch(() => {
this.setIsLoading(false)
this.handleAlert('updateError', 'danger')
})
},
The problem is, the code after catch runs if there's an error inside then. But this way I don't know whether the catch error is an request error or and error triggered by the code inside then.
I'm trying try and catch to solve that problem:
handleUpdatePanorama (panorama) {
try {
const payload = {}
const resp = await this.updatePanorama(payload)
console.log('resp:', resp)
this.setIsLoading(false)
this.handleAlert('updateSuccess', 'success')
} catch (err) {
this.setIsLoading(false)
this.handleAlert('updateError', 'danger')
})
},
However, I get an unexpected token error in this line: await this.updatePanorama(payload)
What am I doing wrong?
The problem is, the code after catch runs if there's an error inside then
The solution for that is to not use catch, but the second then parameter. Have a look at the difference between .then(…).catch(…) and .then(…, …) for details.
I'm trying try and catch to solve that problem
That won't work, the catch clause will still be called if there's an exception thrown by setIsLoading or handleAlert.
I get an unexpected token error. What am I doing wrong?
You have not declared the handleUpdatePanorama method as async.
To mitigate the issues and fix the syntax, you could write
async handleUpdatePanorama (panorama) {
var result
try {
const payload = {}
const resp = await this.updatePanorama(payload)
console.log('resp:', resp)
result = ['updateSuccess', 'success']
} catch (err) {
result = ['updateError', 'danger']
} finally {
this.setIsLoading(false)
}
this.handleAlert(...result)
},
If you need to handle errors specifically from updatePanorama, use the second argument to .then(onSuccess, onError)
handleUpdatePanorama(panorama) {
const payload = {}
this.updatePanorama(payload).then(resp => {
this.setIsLoading(false)
this.handleAlert('updateSuccess', 'success')
}, err => {
// handle error from updatePanorama
// you can throw err if you also want to handle the error in .catch()
}).catch(() => {
this.setIsLoading(false)
this.handleAlert('updateError', 'danger')
})
}
note: if you return (or have no return statement) from the error handler, any subsequent .then(onSuccess will execute, if you throw an error (or return Promise.reject() for example, then the .catch() code will also run