How to handle errors from HTTP observables - javascript

I am not sure how to handle http errors in an angular2 observable.
What I have is
getContextAddress() : Observable<string[]> {
return this.http.get(this.patientContextProviderURL)
.map((res:Response) => {
return res.json()
})
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
}
in valid URL cases, i.e. response of 200, all is well.
I am not sure why when I use a bad url, response code of 400, there is no error generate.
How do I handle that case?

This code is simply re-throwing the error. Are you catching it somewhere?
For example, I have code like this:
this.productService.getProducts()
.subscribe(products => this.products = products,
error => this.errorMessage = <any>error);
}
This catches the thrown error and sets an errorMessage property that I then display in the UI.

I was not catching the error where the subscription was taking place.
My mistake

Related

Jest mockRejectedValue throws unhandled promise rejection in node

I'm trying to write a test in jest but keep getting UnhandledPromiseRejectionWarning when I try to use mockRejectedValue
The code looks like this:
it('Should set error message when call fails', async () => {
const context = mockActionsContext();
const user = {
username: 'alice',
password: 'password'
};
const getError = new Error('network error');
(AuthService.login as jest.Mock) = jest.fn().mockRejectedValue(getError);
await actions[ActionTypes.USER_LOGIN](context, user);
// Check is the commits are called
expect((context.commit as any).mock.calls).toEqual([
[MutationTypes.USER_LOGIN],
[MutationTypes.USER_LOGIN_ERROR, 'Oops, something went wrong. Try again later!']
]);
// Login service is called with user login
expect(AuthService.login as jest.Mock).toHaveBeenCalledWith(user);
});
The AuthService.login returns an axios.post which I try to overwrite with a mock.
actions[ActionTypes.USER_LOGIN](context, user) calls the Authservice.login
The test is passing but I don't want any unhandled promise rejection. Anybody an idea how to fix it?
Edit
#goodmorningasif thanks for your reply.
I've been looking at it too long I thing :)
The action looks as following:
[ActionTypes.USER_LOGIN]: ({ commit }: Context, payload: User) => {
return new Promise((resolve, reject) => {
commit(MutationTypes.USER_LOGIN);
AuthService.login(payload)
.then((token) => {
commit(MutationTypes.USER_LOGIN_SUCCESS, token);
localStorage.setItem('user-token', token);
client.defaults.headers.common.Authorization = `Bearer ${token}`;
resolve(token);
})
.catch((error) => {
let errorMessage = 'Oops, something went wrong. Try again later!';
if (error?.response?.status === 401) {
errorMessage = 'Unknown username and password combination!';
}
localStorage.removeItem('user-token');
commit(MutationTypes.USER_LOGIN_ERROR, errorMessage);
reject(error);
});
});
},
SOLUTION
In my case the action is returning a promise witch would get rejected. In the test, I'm calling the action directly and not catching the rejection.
await actions[ActionTypes.USER_LOGIN](context, user).catch(() => null);
This fixed it.
Can we see the actions and reducer code? It's possible that there's an error in your error :)
You're testing that the login function is called and the action returns the error message you set but you're making an assumption about what causes the error. Maybe it's not because of the mockRejectedValue/'network error'.
I'd suggest including the actual error message in the action payload as well as your error message: one is for developers and debugging and one is for the user to know what to do next.
I also found this helpful on understanding UnhandledPromiseRejectionWarning: https://thecodebarbarian.com/unhandled-promise-rejections-in-node.js.html
Good instinct to figure out the issue and not be content with the test passing, by the way!

Retrieve the React Fetch Error Text for Different Error Results

So, I am fetching a url and my API returns either the data or a 500 error with a few error codes. I am trying to capture the error code and display it in React. In the console, I see the error, but it looks like this:
So, I see the 'Not Found' which is the text I want to display, but how do I get this text out of the error format so I can use it elsewhere?
Here is my code, hopefully this make sense what I am trying to do:
callApi = async (url) => {
const response = await fetch(url);
const body = await response.json();
if (response.status !== 200) throw Error(body.messages);
return body;
};
this.callApi(url)
.then(results => {
this.function(results);
})
.catch(err => {
console.log(err);
if (err === "Not Found") {
console.log('Not Found')
}
if (err === "No Access") {
console.log('No Access')
}
});
JavaScript errors inherit from the base Error object. This means they will almost always have a set message property, meaning you can simply do:
console.log(err.message);
Also to be clear, fetch is a browser API, and has nothing to do with React.

Error Handling in Services - Angular 4

This question is more about the best practice for error handling within Angular 4.
After the reading the Angular 4 documentation I've found that all error handling should be done in the Services and the Components don't need to worry about that.
Currently I handle the error within the my subscribe call to my observable.
logIn(){
this._userService.logIn({"email":this.loginForm.value.email,"password": this.loginForm.value.password})
.subscribe(
data => {//handle data here},
(err: HttpErrorResponse) => {
if (err.error instanceof Error) {
// A client-side or network error occurred. Handle it accordingly.
console.log('An error occurred:', err.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.log(`Backend returned code ${err.status}, body was: ${err.error.message}`);
}
}
)}
Im struggling to handle errors within Data Service. Im looking for some professional advice on how I can handle errors properly.
My Data Service:
logIn(body): Observable<any>{
return this._http.post('http://localhost:3000/api/login', body)
.do(data => console.log('Login: ' + JSON.stringify(data)))
}
You can use catch and throw operator to preprocess the error (there is also finally operator btw):
logIn(body): Observable<any> {
return this._http
.post('http://localhost:3000/api/login', body)
.do(data => console.log('Login: ' + JSON.stringify(data)))
.catch(err => {
// do whatever you want when error occurres
console.log(err);
// re-throw error so you can catch it when subscribing, fallback to generic error code
return Observable.throw(err.message.toUpperCase() || 'API_ERROR');
});
}
You will need to import those first to use them.
Sometimes it can be error that you want to ignore, then just return Observable.of(false); instead of the Observable.throw().
You should do this only for error pre-processing. You will still need to catch it later in the template (therefore we need to re-throw it), if you want to adjust the template based on it. But the code in component should ideally just catch the error and assign it to template (or show alert or whatever).
logIn() {
this._userService.logIn({...})
.subscribe(
data => this.data = data, // process data
(err: string) => this.error = err // process error
)
}

Catch() not handling 404

I'm making a script to fetch some data from my api:
const success = (response) => {
console.log(response);
};
const failed = (error) => {
console.log(error);
};
axios.$http.get('/somedata')
.then((response) => {
success(response.data);
})
.catch((error) => {
failed(error);
});
/somepage is a non-existing page so it returns a 404. But the catch is not handling this. Why not? In my console I have the error TypeError: Cannot read property 'data' of undefined. Why does it not run the failed() function? I don't understand.
Found out it was related to a custom interceptor handling 401-errors (but not 404 errors)...
Judging by the error message, it looks like "success(response.data);" is being called. Is it possible the server is successfully returning a page that says something like "Error 404" rather than actually returning http response code 404?
You could impliment a check for 404s.
axios.$http.get('/somedata')
.then(response => {
if(response.status !== 404) //or any status code really
success(response.data);
else
failed(response)
})
.catch((error) => {
failed(error);
});
Then again what you probably want to check for is to make sure it's a 200 that returns.
axios.$http.get('/somedata')
.then(response => {
if(response.status === 200)
success(response.data);
else
failed(response)
})
.catch((error) => {
failed(error);
});

RxJS: Handing errors in cycle.js custom driver

I have implemented some error handling code in my main function as below. It uses the catch operator to filter and report on errors in one stream and ignore them in another. This allows me to know about and report on errors that occur with requests whilst not failing the entire stream so that subsequent requests can continue.
For reasons that might not be apparent in the code snippets below I am impementing a custom driver to request and handle data. I'm not using the cycle http driver.
Here's my code that successfully reports an error:
function main(sources) {
// Catch driver errors so they can be logged
const error$ = sources.CustomDriver
.map(x => x.catch(e => Rx.Observable.just(e)))
.flatMap(p => p)
// Filter out the errors to deal with requests that did not fail
const data$ = sources.CustomDriver
.map(x => x.catch(e => Rx.Observable.empty()))
.flatMap(p => p)
return {
CustomDriver: Rx.Observable.just('initial event'),
Log: data$,
Error: error$
}
}
Cycle.run(main, {
CustomDriver: makeCustomDriver(),
Log: msg$ => { msg$.subscribe(
msg => console.log('LOG: ', msg),
err => console.log('problem with Log driver: ', err),
() => console.log('Log Completed')
) },
Error: msg$ => { msg$.subscribe(
e => console.log('ERR: ', e),
err => console.log('problem with Error driver:', err),
() => console.log('Error Completed')
) }
})
function makeCustomDriver() {
return function customDriver(requests$) {
return requests$
.map(request => Rx.Observable.fromPromise(makeFailedRequest()))
}
}
function makeFailedRequest() {
console.log('some API request')
return Promise.reject('error')
}
The output is as follows:
some API request
some API request
Log Completed
ERR: error
Error Completed
On the plus side the error is reported. However, the API request is actually made twice, which is not what I expected to happen initially.
After learning some more RxJS and getting a better understanding of Hot and Cold observables I realised that I was creating two subscriptions to the CustomDriver stream (one for error$ and one for data$) and because the CustomDriver Observable was cold it would repeat the Observable.just for each subscriber.
So I tried to make my CustomDriver Observavble hot with share:
function makeCustomDriver() {
return function customDriver(requests$) {
return requests$
.map(request => Rx.Observable.fromPromise(makeFailedRequest()))
.share()
}
}
With that change, the output is as follows:
some API request
Error Completed
Log Completed
So I managed to get rid of the duplicate request but the error was swallowed up in the process.
What is happening with share that causes the errors to be lost and how can I avoid duplicate requests without losing errors?
.shareReplay(1) appears to give the desired result.
There is a factory for making custom drivers of that kind that you want (from Promises) https://github.com/whitecolor/cycle-async-driver it includes, helpers for dealing with errors (success and failure).
You can created drivers just like that:
import {makeAsyncDriver} from 'cycle-async-driver'
customDriver = makeAsyncDriver(
(request) => requestHanderThatReturnsPromise(reques)
)

Categories

Resources