The use of throwError(error) has been phased out, however, no replacement has been introduced for the Error(HttpErrorResponse)

It seems like the use of throwError(error) is deprecated now. VS Code suggests using

throwError(() => new Error('error'))
instead, as new Error(...) only accepts strings. How can I replace it correctly without causing issues with my HttpErrorHandlerService?

http-error.interceptor.ts

import { Injectable } from '@angular/core';
import {
  HttpEvent,
  HttpInterceptor,
  HttpHandler,
  HttpRequest,
  HttpErrorResponse,
  HttpResponse,
  HttpHeaders
} from '@angular/common/http';
import { Observable, EMPTY, finalize, catchError, timeout, map, throwError } from 'rxjs';

import { HttpErrorHandlerService } from '@core/services';

@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
  private readonly APP_XHR_TIMEOUT = 6000;

  constructor(private errorHandlerService: HttpErrorHandlerService) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(this.performRequest(request)).pipe(
      timeout(this.APP_XHR_TIMEOUT),
      map((event: HttpEvent<any>) => this.handleSuccessfulResponse(event)),
      catchError((error: HttpErrorResponse) => this.processRequestError(error, request, next)),
      finalize(this.handleRequestCompleted.bind(this))
    );
  }

  private performRequest(request: HttpRequest<any>): HttpRequest<any> {
    let headers: HttpHeaders = request.headers;
    //headers = headers.set('MyCustomHeaderKey', `MyCustomHeaderValue`);
    return request.clone({ headers });
  }

  private handleSuccessfulResponse(event: HttpEvent<any>): HttpEvent<any> {
    if (event instanceof HttpResponse) {
      event = event.clone({ body: event.body.response });
    }
    return event;
  }

  private processRequestError(
    error: HttpErrorResponse,
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    console.log('http error response');

    if ([401].includes(error.status)) {
      return throwError(error);
    }

    this.errorHandlerService.handle(error);

    return throwError(error);
  }

  private handleRequestCompleted(): void {
    // console.log(`Request finished`);
  }
}

import { Injectable } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';

import { MessageService } from 'primeng/api';
import { TimeoutError } from 'rxjs';

/**
 * Shows a user-friendly error message when a HTTP request fails.
 */
@Injectable({
  providedIn: 'root'
})
export class HttpErrorHandlerService {
  constructor(private messageService: MessageService) {}

  handle(error: Error | HttpErrorResponse) {
    if (error instanceof TimeoutError) {
      return this.openDialog('error', `Няма връзка до сървъра.`);
    }

    if (error instanceof HttpErrorResponse && error.error && error.error.message) {
      return this.openDialog('error', error.error.message);
    }

    if (error instanceof Error) {
      switch (error.message) {
        default:
          return this.openDialog('error', `An unknown error occurred`);
      }
    }

    // Generic HTTP errors
    switch (error.status) {
      case 400:
        switch (error.error) {
          case 'invalid_username_or_password':
            return this.openDialog('error', 'Невалидно потребителско име или парола');
          default:
            return this.openDialog('error', 'Bad request');
        }

      case 401:
        return this.openDialog('error', 'Ще трябва да се логнете отново');

      case 403:
        return this.openDialog('error', `You don't have the required permissions`);

      case 404:
        return this.openDialog('error', 'Resource not found');

      case 422:
        return this.openDialog('error', 'Invalid data provided');

      case 500:
      case 501:
      case 502:
      case 503:
        return this.openDialog('error', 'An internal server error occurred');

      case -1:
        return this.openDialog(
          'error',
          'You appear to be offline. Please check your internet connection and try again.'
        );

      case 0:
        return this.openDialog('error', `CORS issue?`);

      default:
        return this.openDialog('error', `An unknown error occurred`);
    }
  }

  private openDialog(severity: string, message: string) {
    if (message?.trim()) {
      this.messageService.add({
        key: 'interceptor',
        severity: severity,
        summary: 'Информация',
        detail: message,
        life: 3000
      });
    }
  }
}

auth.interceptor.ts

import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
  HttpErrorResponse
} from '@angular/common/http';
import {
  BehaviorSubject,
  catchError,
  EMPTY,
  filter,
  finalize,
  Observable,
  switchMap,
  take,
  throwError
} from 'rxjs';

import { AuthService } from '@core/services';
import { AuthResponse } from '@core/types';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  private refreshTokenInProgress: boolean;
  private refreshToken$ = new BehaviorSubject<AuthResponse | null>(null);

  constructor(private authService: AuthService) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next
      .handle(this.performRequest(request))
      .pipe(
        catchError((error: HttpErrorResponse) => this.processRequestError(error, request, next))
      );
  }

  private performRequest(request: HttpRequest<any>): HttpRequest<any> {
    const accessToken = this.authService.getAccessToken();

    let headers = request.headers;
    if (accessToken) {
      headers = headers.set('Authorization', `Bearer ${accessToken}`);
    }

    return request.clone({ headers });
  }

  private processRequestError(
    error: HttpErrorResponse,
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {
    console.log('auth interceptor called');

    if (
      error instanceof HttpErrorResponse &&
      error.status === 401 &&
      this.authService.isSignedIn()
    ) {
      return this.refreshToken(request, next);
    }

    return throwError(error);
  }

  private refreshToken(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    console.log('refresh token in auth.interceptor called');

    // in case the page consists of more than one requests
    if (!this.refreshTokenInProgress) {
      this.refreshToken$.next(null);
      this.refreshTokenInProgress = true;

      return this.authService.refreshToken().pipe(
        switchMap((response) => {
          if (response) {
            this.refreshToken$.next(response);
            return next.handle(this.performRequest(request));
          }

          this.authService.signOut();
          return throwError(() => new Error("RefreshTokenFailed"));
        }),
        catchError((error) => {
          this.authService.signOut();
          return throwError(error);
        }),
        finalize(() => (this.refreshTokenInProgress = false))
      );
    } else {
      // wait while getting new token
      return this.refreshToken$.pipe(
        filter((result) => result !== null),
        take(1),
        switchMap(() => next.handle(this.performRequest(request)))
      );
    }
  }
}

Answer №1

Instead of using the code block below:

    catchError((error) => {
      this.authService.signOut();
      return throwError(error);
    }),

You might want to consider trying out this alternative:

    catchError((error) => {
      this.authService.signOut();
      return throwError(() => error);
    }),

I haven't had a chance to extensively test it, but a preliminary trial seemed successful.

To demonstrate, I conducted a simple test with RxJS version 7.2:

Service

  getProducts(): Observable<IProduct[]> {
    return this.http.get<IProduct[]>(this.productUrl)
      .pipe(
        tap(data => console.log('All: ', JSON.stringify(data))),
        catchError(this.handleError)
      );
  }

  private handleError(err: HttpErrorResponse): Observable<never> {
    // just a test ... more could would go here
    return throwError(() => err);
  }

Note that err in the example is of type HttpErrorResponse.

Component

  ngOnInit(): void {
    this.sub = this.productService.getProducts().subscribe({
      next: products => {
        this.products = products;
        this.filteredProducts = this.products;
      },
      error: err => this.errorMessage = err.message
    });
  }

In this scenario, I successfully retrieved the message property from the error response and displayed it on my user interface.

Please let me know if this solution works for you.

Answer №2

Just utilize:

return new Error(message);

Avoid using unnecessarily

return throwError(() => new Error(message));

Check out some examples here

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Can you tell me the data type of a Babel plugin parameter specified in TypeScript?

Struggling to find ample examples or documentation on writing a Babel plugin in TypeScript. Currently, I am working on a visitor plugin with the following signature: export default function myPlugin({ types: t }: typeof babel): PluginObj { In order to obt ...

I'm having trouble getting my nav-tab to function properly in Bootstrap 4 when used with Angular 4. Can

I have been attempting to utilize Bootstrap 4 tabs with Angular 4 with the expectation that it would resemble and function similar to this example enter link description here However, my page is displaying like this. https://i.sstatic.net/dymNH.png Here ...

Tips for concealing a parent within a complexly nested react router structure

Is there a more efficient way to conceal or prevent the rendering of parent content within a react router object? I could use conditional rendering, but I'm unsure if that's the optimal solution. My setup involves a parent, child, and grandchild, ...

Testing a React component that uses useParams: A step-by-step guide

I've been working on creating a BBS App using TypeScript, React, React Router, and React Testing Library. However, I've encountered an issue where a component utilizing useParams is not passing a test. Interestingly, it seems to be working correc ...

Validating Angular input for decimal values based on specific criteria

Is there a way to limit user input to numbers with only 2 decimal places if both itemNew.UL_DATA and itemNew.LL_DATA are equal to 0 or blank? <ion-col col-2> <input (keypress)="ShowKey();" [style.display]="itemNew.UL_DATA== ...

To properly display a URL on a webpage using Angular, it is necessary to decode

After my console.log in Angular 5 service call to the component, I can see the correct data URL being displayed http://localhost:4200/inquiry?UserID=645 However, when it is inside an Angular for loop in the HTML template, it displays http://localhost:42 ...

transform an array encoded in base64 format into a JSON object

I am encountering an issue where the base64 array I'm trying to send to an API is coming up empty. Here's a breakdown of what I'm doing: Firstly, I have an array of files with images in my code: [0: File {name: '766_generated.jpg' ...

Step-by-step guide on crafting a personalized button component in Angular 2 and above with the help of ControlValueAccessor

Are you looking to create a custom button component in Angular versions 2 and above using ControlValueAccessor? Do you need help handling click button and focus events within your custom button? Check out the following sample code: import { Component, OnI ...

Accessing Next and Previous Elements Dynamically in TypeScript from a Dictionary or Array

I am new to Angular and currently working on an Angular 5 application. I have a task that involves retrieving the next or previous item from a dictionary (model) for navigation purposes. After researching several articles, I have devised the following solu ...

Angular 6's removeAt and trackBy functions are causing issues in updating the formArray and not reflecting the

Within a formGroup, I am adding and deleting elements from a formArray. To keep track of the ids to delete, I am using trackBy. When calling the function delete(i), it successfully removes the correct element from the formArray in the form. However, in th ...

What is the best way to reset a setInterval ID in Angular?

In my Angular project, I am developing a simple timer functionality that consists of two buttons – one button to start the timer and another to stop it. The timer uses setInterval with a delay of 1000ms. However, when the stop button is pressed, the time ...

Unnecessary Attributes in Type that Should be Automatically Inherited by Child Component

Within my child component, I am creating the Props interface and incorporating it into the React.Component. These Props must then be passed from the parent component to the child component. So far, everything is clear and logical. However, when I extend ...

Encountering difficulty importing a module from a different module within Nuxt

Within my Nuxt project directory, there exists a specific folder named modules which houses my customized modules. For this illustration, it includes the modules foo and bar. The inclusion of foo in the nuxt.config.js file appears as follows: // nuxt.confi ...

Conditional generic type in return type with Typescript

How can I condition the generic return type when a value is not present? type Foo = {}; class Bar<P extends Foo> { static make<P extends Foo>(a?: P): Bar<P> { return new Bar(); } } Bar.make() // returns Bar<Foo> ...

Verifying User Permissions with Angular 2/4 and API

I am currently in the process of developing an Angular 2/4 application that interacts with an API built in Laravel 5.4. One area I'm seeking guidance on involves checking authentication and permissions on the backend through Angular. I want to verify ...

Guide on deploying an Angular 7 frontend paired with a Spring Boot backend

Having trouble with integrating my Angular application with Spring Boot REST backend. Can anyone suggest a simple method to run both on the same localhost port? ...

You appear to be missing a dependency on either "@angular/core" or "rxjs" when attempting to deploy your MEAN app on Heroku. This issue must be resolved

I encountered an issue while trying to deploy my MEAN-stack application on Heroku. My app was built mainly following this tutorial series. However, during the deployment process by pushing to GIT, I received the following error: <a href="/cdn-cgi/l/emai ...

Exploring the power of makeStyles in Material UI when combined with TypeScript

I am currently in the process of converting a JavaScript template to Typescript. Here is an example of my accordionStyle.ts file: import { primaryColor, grayColor } from "../../material-dashboard-pro-react"; const accordionStyle = (theme?:an ...

I am attempting to store the primary array in local storage, but unfortunately, the value is not being saved within the React context API

I attempted to store the main array in local storage and retrieve it as global state, but I am facing an issue where the data is not being saved in the local storage. This file represents my context. import { createContext, useReducer, ReactNode, FC, use ...

Previous states in TypeScript

Just starting out with typescript and trying to work with user files in order to update the state. Currently facing a typescript error that I can't seem to figure out - Error message: Argument of type '(prev: never[]) => any[]' is not as ...