#catching specific errors, others to errorHandler

1 messages · Page 1 of 1 (latest)

chilly nimbus
#

Hi guys I need some advice about how to implement throwing/catching errors nicely.
IMO components shouldn't care about the source of data; they are interested in data and the errors that can occur only.
So handling HttpErrorResponse with their status-code is something that should be take care by the service.
Unhandled errors should go through the CustomErrorHandler
Is the code below the way to go then (thread)

#
Service:

class FooNotFoundError {
    constructor(readonly reason: string) { }
}

class FooDoesntLikeCoffeeError {
    
}

getFoo() {
    return this.httpClient.get('some-url')
    .pipe(
        map(response => response.foo),
        catchError(error => {
            if (error instanceof HttpErrorResponse && error.status === 404) {
                return throwError(() => new FooNotFoundError('some reason thats optional in the payload'));
            } elseif (error instanceof HttpErrorResponse && error.status === 418) {
                return throwError(() => new FooDoesntLikeCoffeeError());
            }
            return throwError(() => error);
        })
    );
}

Component:

this.service.getFoo().subscribe({
    next: foo => console.log(foo),
    error: error => {
        if (error instanceof FooNotFoundError) {
            // handle this kind of error
        } else if (error instanceof FooDoesntLikeCoffeeError) {
            // handle this kind of error
        }
        throw error;
    }
})


CustomErrorHandler:

export class CustomErrorHandler extends ErrorHandler {
    override handleError(error:any) {
        // route to some error-page or open error-dialog
        // or to a maintenance page when 503 for example
    }
}
lunar frost
#

Overall, the approach you have described for handling errors in your code looks reasonable. It's a good idea to have a service that handles HTTP errors and maps them to specific error types that are more meaningful in the context of your application.

It's also a good idea to have a component that is responsible for handling errors specific to its own logic, such as displaying a message to the user or routing to a different page. This way, the component doesn't need to be concerned with the details of the error or how it was generated, and can simply focus on reacting to the error in an appropriate way.

Finally, having a global error handler can be useful for handling errors that are not caught by the service or the component. This can be especially useful for logging errors or displaying a default error message to the user.

One suggestion for improvement would be to consider using a switch statement in the service to handle different HTTP error codes, rather than using multiple if statements. This can make the code easier to read and maintain. Additionally, you might want to consider adding additional error handling logic to your service, such as retrying failed HTTP requests or handling server-side errors that do not generate an HTTP error response.

chilly nimbus
#

@lunar frost thanks for you reply, seems like we're on the right way. Retry-logic is something I want to implement in the http-interceptor. I dislike I need to manually rethrow the error in the component, that's something that can be forgotten quickly. It would be great if it's possible to receive only the FooNotFoundError and FooDoesntLikeCoffeeError in the error-callback of the subscribe method and all other errors are passed to the error-handler directly

lunar frost
# chilly nimbus <@703277378356379709> thanks for you reply, seems like we're on the right way. R...

You can implement this behavior by using the catchError operator in the service to catch all errors, and then returning either a specific error or the original error, depending on the type of error that occurred.

Here's an example of how you could modify the service to only return specific errors to the component, and pass all other errors to the error handler:

import { throwError, Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';

class FooNotFoundError {
  constructor(readonly reason: string) {}
}

class FooDoesntLikeCoffeeError {
  // ...
}

getFoo(): Observable<any> {
  return this.httpClient.get('some-url').pipe(
    catchError((error: HttpErrorResponse) => {
      switch (error.status) {
        case 404:
          return throwError(new FooNotFoundError('some reason thats optional in the payload'));
        case 418:
          return throwError(new FooDoesntLikeCoffeeError());
        default:
          // Pass all other errors to the error handler
          return throwError(error);
      }
    })
  );
}

Then, in the component, you can handle the specific errors by using the instanceof operator to check the type of the error:

this.service.getFoo().subscribe({
  next: foo => console.log(foo),
  error: error => {
    if (error instanceof FooNotFoundError) {
      // handle this kind of error
    } else if (error instanceof FooDoesntLikeCoffeeError) {
      // handle this kind of error
    }
  }
});

All other errors will be passed to the error handler, which can handle them as needed.

chilly nimbus
#

@lunar frost I need to add throw error; in the component, otherwise the error-handler won't be triggered (see my first example)