Error Handling in Services - Angular 4 - javascript

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

Related

How to catch all internal errors from external code in JavaScript?

I have the following call to an API (an npm module running in Node.js) in a JavaScript file in which I would like to catch all errors so can gracefully handle them. But if I e.g. pass a bad API-KEY or a city name that does not exist, there is an error in the internal code of the API which is not caught by the try/catch:
const weather = require('openweather-apis');
const getTemperature = (city, cbSuccess, cbFailure) => {
try {
weather.setLang('de');
weather.setCity(city);
weather.setUnits('metric');
weather.setAPPID('BADKEY');
weather.getTemperature((err, temperature) => {
if (err) {
console.log(err);
} else {
console.log(`The temperature in ${city} is ${temperature}° C.`);
}
});
} catch (error) {
console.log('there was an error');
}
}
getTemperature('Berlin');
Rather, an error is displayed and execution stops:
C:\edward\nwo\jsasync\node_modules\openweather-apis\index.js:162
return callback(err,jsonObj.main.temp);
^
TypeError: Cannot read property 'temp' of undefined
at C:\edward\nwo\jsasync\node_modules\openweather-apis\index.js:162:40
at IncomingMessage.<anonymous> (C:\edward\nwo\jsasync\node_modules\openweather-apis\index.js:250:18)
at IncomingMessage.emit (events.js:194:15)
at endReadableNT (_stream_readable.js:1125:12)
at process._tickCallback (internal/process/next_tick.js:63:19)
Is there a way in JavaScript to catch all errors as one does in e.g. Java and C#?
I believe that something like this might work:
async execute(weather, city, temperature) {
return await new Promise(function(resolve, reject) {
weather.getTemperature((err, temperature) => {
if (err) {
reject(err);
} else {
resolve(`The temperature in ${city} is ${temperature}° C.`);
}
});
};
}
const getTemperature = async (city, cbSuccess, cbFailure) => {
try {
weather.setLang('de');
weather.setCity(city);
weather.setUnits('metric');
weather.setAPPID('BADKEY');
const res = await execute(weather, city, temperature);
console.log(res);
} catch (error) {
console.log('there was an error');
}
}
You're out of luck if an exception throws in asynchronous code. This will stop execution of the script (as you're seeing above).
The module you are using should possibly handle the error in a better way and pass the error in the callback err parameter. Unless you fork the code or file a bug you're stuck with this.
The same effect can be demonstrated here:
async function testAsyncException() {
try {
setTimeout(() => {
throw new Error("Error in asynchronous code");
}, 100);
} catch (e) {
// This will never be caught...
console.error("testAsyncException: A bad error occurred:", e);
}
}
process.on('uncaughtException', (e) => {
console.log("uncaughtException:", e);
})
testAsyncException();
The try .. catch block around the setTimeout call will not handle the generated exception.
The only way you can "catch" this type of exception is using a process event like so:
process.on('uncaughtException', (e) => {
console.log("uncaughtException:", e);
})
This however should only be used to log and then exit. Trying to recover program state at this point is not a good idea, since the application is in an unknown state.
If you're using a process manager such as the very useful PM2, the script can be automatically restarted on errors.
Conversely if we try the following:
function testSyncException() {
try {
throw new Error("Error in synchronous code");
} catch (e) {
// This will be caught...
console.error("testSyncException: A bad error occurred:", e);
}
}
testSyncException();
We can see that the exception will be caught.
I strongly recommend this excellent article on error handling by the creators of Node.js (Joyent):
https://www.joyent.com/node-js/production/design/errors
It details the best strategies for handling both Operational errors and Programmer errors.
there is an error in the internal code of the API
return callback(err,jsonObj.main.temp);
^
TypeError: Cannot read property 'temp' of undefined
at C:\edward\nwo\jsasync\node_modules\openweather-apis\index.js:162:40
This is clearly a bug in the openweather-apis library. Report it. You hardly will be able to work around it. The library will need to check whether jsonObj and jsonObj.main exist before attempting to access .temp on it, and it should call your callback with an error if the jsonObj doesn't look as expected.

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.

How to handle errors from HTTP observables

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

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

correct way to break promise chain on first rejection [duplicate]

I am still fairly new to promises and am using bluebird currently, however I have a scenario where I am not quite sure how to best deal with it.
So for example I have a promise chain within an express app like so:
repository.Query(getAccountByIdQuery)
.catch(function(error){
res.status(404).send({ error: "No account found with this Id" });
})
.then(convertDocumentToModel)
.then(verifyOldPassword)
.catch(function(error) {
res.status(406).send({ OldPassword: error });
})
.then(changePassword)
.then(function(){
res.status(200).send();
})
.catch(function(error){
console.log(error);
res.status(500).send({ error: "Unable to change password" });
});
So the behaviour I am after is:
Goes to get account by Id
If there is a rejection at this point, bomb out and return an error
If there is no error convert the document returned to a model
Verify the password with the database document
If the passwords dont match then bomb out and return a different error
If there is no error change the passwords
Then return success
If anything else went wrong, return a 500
So currently catches do not seem to stop the chaining, and that makes sense, so I am wondering if there is a way for me to somehow force the chain to stop at a certain point based upon the errors, or if there is a better way to structure this to get some form of branching behaviour, as there is a case of if X do Y else Z.
Any help would be great.
This behavior is exactly like a synchronous throw:
try{
throw new Error();
} catch(e){
// handle
}
// this code will run, since you recovered from the error!
That's half of the point of .catch - to be able to recover from errors. It might be desirable to rethrow to signal the state is still an error:
try{
throw new Error();
} catch(e){
// handle
throw e; // or a wrapper over e so we know it wasn't handled
}
// this code will not run
However, this alone won't work in your case since the error be caught by a later handler. The real issue here is that generalized "HANDLE ANYTHING" error handlers are a bad practice in general and are extremely frowned upon in other programming languages and ecosystems. For this reason Bluebird offers typed and predicate catches.
The added advantage is that your business logic does not (and shouldn't) have to be aware of the request/response cycle at all. It is not the query's responsibility to decide which HTTP status and error the client gets and later as your app grows you might want to separate the business logic (how to query your DB and how to process your data) from what you send to the client (what http status code, what text and what response).
Here is how I'd write your code.
First, I'd get .Query to throw a NoSuchAccountError, I'd subclass it from Promise.OperationalError which Bluebird already provides. If you're unsure how to subclass an error let me know.
I'd additionally subclass it for AuthenticationError and then do something like:
function changePassword(queryDataEtc){
return repository.Query(getAccountByIdQuery)
.then(convertDocumentToModel)
.then(verifyOldPassword)
.then(changePassword);
}
As you can see - it's very clean and you can read the text like an instruction manual of what happens in the process. It is also separated from the request/response.
Now, I'd call it from the route handler as such:
changePassword(params)
.catch(NoSuchAccountError, function(e){
res.status(404).send({ error: "No account found with this Id" });
}).catch(AuthenticationError, function(e){
res.status(406).send({ OldPassword: error });
}).error(function(e){ // catches any remaining operational errors
res.status(500).send({ error: "Unable to change password" });
}).catch(function(e){
res.status(500).send({ error: "Unknown internal server error" });
});
This way, the logic is all in one place and the decision of how to handle errors to the client is all in one place and they don't clutter eachother.
.catch works like the try-catch statement, which means you only need one catch at the end:
repository.Query(getAccountByIdQuery)
.then(convertDocumentToModel)
.then(verifyOldPassword)
.then(changePassword)
.then(function(){
res.status(200).send();
})
.catch(function(error) {
if (/*see if error is not found error*/) {
res.status(404).send({ error: "No account found with this Id" });
} else if (/*see if error is verification error*/) {
res.status(406).send({ OldPassword: error });
} else {
console.log(error);
res.status(500).send({ error: "Unable to change password" });
}
});
I am wondering if there is a way for me to somehow force the chain to stop at a certain point based upon the errors
No. You cannot really "end" a chain, unless you throw an exception that bubbles until its end. See Benjamin Gruenbaum's answer for how to do that.
A derivation of his pattern would be not to distinguish error types, but use errors that have statusCode and body fields which can be sent from a single, generic .catch handler. Depending on your application structure, his solution might be cleaner though.
or if there is a better way to structure this to get some form of branching behaviour
Yes, you can do branching with promises. However, this means to leave the chain and "go back" to nesting - just like you'd do in an nested if-else or try-catch statement:
repository.Query(getAccountByIdQuery)
.then(function(account) {
return convertDocumentToModel(account)
.then(verifyOldPassword)
.then(function(verification) {
return changePassword(verification)
.then(function() {
res.status(200).send();
})
}, function(verificationError) {
res.status(406).send({ OldPassword: error });
})
}, function(accountError){
res.status(404).send({ error: "No account found with this Id" });
})
.catch(function(error){
console.log(error);
res.status(500).send({ error: "Unable to change password" });
});
I have been doing this way:
You leave your catch in the end. And just throw an error when it happens midway your chain.
repository.Query(getAccountByIdQuery)
.then((resultOfQuery) => convertDocumentToModel(resultOfQuery)) //inside convertDocumentToModel() you check for empty and then throw new Error('no_account')
.then((model) => verifyOldPassword(model)) //inside convertDocumentToModel() you check for empty and then throw new Error('no_account')
.then(changePassword)
.then(function(){
res.status(200).send();
})
.catch((error) => {
if (error.name === 'no_account'){
res.status(404).send({ error: "No account found with this Id" });
} else if (error.name === 'wrong_old_password'){
res.status(406).send({ OldPassword: error });
} else {
res.status(500).send({ error: "Unable to change password" });
}
});
Your other functions would probably look something like this:
function convertDocumentToModel(resultOfQuery) {
if (!resultOfQuery){
throw new Error('no_account');
} else {
return new Promise(function(resolve) {
//do stuff then resolve
resolve(model);
}
}
Probably a little late to the party, but it is possible to nest .catch as shown here:
Mozilla Developer Network - Using Promises
Edit: I submitted this because it provides the asked functionality in general. However it doesn't in this particular case. Because as explained in detail by others already, .catch is supposed to recover the error. You can't, for example, send a response to the client in multiple .catch callbacks because a .catch with no explicit return resolves it with undefined in that case, causing proceeding .then to trigger even though your chain is not really resolved, potentially causing a following .catch to trigger and sending another response to the client, causing an error and likely throwing an UnhandledPromiseRejection your way. I hope this convoluted sentence made some sense to you.
Instead of .then().catch()... you can do .then(resolveFunc, rejectFunc). This promise chain would be better if you handled things along the way. Here is how I would rewrite it:
repository.Query(getAccountByIdQuery)
.then(
convertDocumentToModel,
() => {
res.status(404).send({ error: "No account found with this Id" });
return Promise.reject(null)
}
)
.then(
verifyOldPassword,
() => Promise.reject(null)
)
.then(
changePassword,
(error) => {
if (error != null) {
res.status(406).send({ OldPassword: error });
}
return Promise.Promise.reject(null);
}
)
.then(
_ => res.status(200).send(),
error => {
if (error != null) {
console.error(error);
res.status(500).send({ error: "Unable to change password" });
}
}
);
Note: The if (error != null) is a bit of a hack to interact with the most recent error.
I think Benjamin Gruenbaum's answer above is the best solution for a complex logic sequence, but here is my alternative for simpler situations. I just use an errorEncountered flag along with return Promise.reject() to skip any subsequent then or catch statements. So it would look like this:
let errorEncountered = false;
someCall({
/* do stuff */
})
.catch({
/* handle error from someCall*/
errorEncountered = true;
return Promise.reject();
})
.then({
/* do other stuff */
/* this is skipped if the preceding catch was triggered, due to Promise.reject */
})
.catch({
if (errorEncountered) {
return;
}
/* handle error from preceding then, if it was executed */
/* if the preceding catch was executed, this is skipped due to the errorEncountered flag */
});
If you have more than two then/catch pairs, you should probably use Benjamin Gruenbaum's solution. But this works for a simple set-up.
Note that the final catch only has return; rather than return Promise.reject();, because there's no subsequent then that we need to skip, and it would count as an unhandled Promise rejection, which Node doesn't like. As is written above, the final catch will return a peacefully resolved Promise.
I wanted to preserve the branching behaviour that Bergi's answer had, yet still provide the clean code structure of unnested .then()'s
If you can handle some ugliness in the machinery that makes this code work, the result is a clean code structure similar to non-nested chained .then()'s
One nice part of structuring a chain like this, is that you can handle all the potential results in one place by chainRequests(...).then(handleAllPotentialResults) this might be nice if you need to hide the request chain behind some standardised interface.
const log = console.log;
const chainRequest = (stepFunction, step) => (response) => {
if (response.status === 200) {
return stepFunction(response, step);
}
else {
log(`Failure at step: ${step}`);
return response;
}
};
const chainRequests = (initialRequest, ...steps) => {
const recurs = (step) => (response) => {
const incStep = step + 1;
const nextStep = steps.shift();
return nextStep ? nextStep(response, step).then(chainRequest(recurs(incStep), incStep)) : response;
};
return initialRequest().then(recurs(0));
};
// Usage
async function workingExample() {
return await chainRequests(
() => fetch('https://jsonplaceholder.typicode.com/users'),
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/'); },
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/3'); }
);
}
async function failureExample() {
return await chainRequests(
() => fetch('https://jsonplaceholder.typicode.com/users'),
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/fail'); },
(resp, step) => { log(`step: ${step}`, resp); return fetch('https://jsonplaceholder.typicode.com/posts/3'); }
);
}
console.log(await workingExample());
console.log(await failureExample());
The idea is there, but the interface exposed could probably use some tweaking.
Seeing as this implementation used curried arrow functions, the above could potentially be implemented with more direct async/await code

Categories

Resources