Search code examples
angulartypescriptrxjsangular-httpclient

How to retrieve an Observable<boolean> from Angular httpClient (true if 200, false if 404)


In angular, I have an async validator for a user registration form that checks if an email already exists in database. It calls an endpoint of my Rest API with HEAD method, that returns a Http 200 Ok status if the email is found and a 404 if it's not found (I'm not sure that it's a proper Rest practice thought). But i can't figure out how to turn that http call into an Observable that my validator is expecting.

My layers for this operation without taking in consideration the form is :

AsyncValidator -> authService.isEmailTaken() -> dataManagementService.headCall()

My validator:

export const uniqueEmailValidator = (authService: AuthService, time: number = 500) => {
  return (input: FormControl) => {
    return timer(time).pipe(
      switchMap(() => authService.isEmailTaken(input.value)),
      map(isTaken => {
        console.log(isTaken);
        return isTaken ? { uniqueEmail: true } : null;
      })
    );
  };
};

AuthService.ts:

  public isEmailTaken(email: string): Observable<boolean> {
    const params = new Map();
      params.set('email', email);
      return this.dataManagementService.headResource(
        this.authURI + this.registerURI,
        params
      ).pipe(
        map(
         () => {
           console.log('success');
           return true;
         },
         () => {
           console.log('error');
           return false;
         }
     )
    );
  }

My http call :

  public headResource(url: string, paramMap?: Map<string, string>): Observable<any> {
    const params = this.getHttpParams(paramMap);
    console.log('Sending HEAD request to server :');
    console.log(this.baseUrl + url);
    return this.httpClient.head(
      this.baseUrl + url,
      {params}
    );
  }

My validator should be valid when the status code is 404 and invalid when is 200, but though it works on 200, it has no efffect on 404. I'm not very confortable using rxjs.
Is there something i'm missing ? Or should i change the approach with the backend service sending a boolean, or maybe a 204 status code if not found ?
Thanks a lot for help!

============== EDIT =======================

Thanks to martin comment i managed to make it works by changing the isEmailTaken method of my authService, and using catchError:

  public isEmailTaken(email: string): Observable<boolean> {
    const params = new Map();
    params.set('email', email);
    return this.dataManagementService.headResource(
      this.authURI + this.registerURI,
      params
    ).pipe(
      map(
      () => {
        return true;
      }),
      catchError(error => {
        if (error.status === 404) {
          return of(false);
        }
      })
    );
  }

Solution

  • With Angular's HttpClient 404 response will be turned into an error notification while your validator expect boolean next notification. So the problem is how to turn error notification into next. Luckily, this is very easy with catchError() operator:

    import { of, throwError } from 'rxjs';
    import { catchError } from 'rxjs/operators';
    
    this.httpClient.head().pipe(
      catchError(error => {
        if (error.status === 404) {
          return of(false); // emit `false` as next notification instead of the original error
        }
        return throwError(error); // rethrow other status codes as error
      }),
    );
    

    Btw, map works only with next notifications. I doesn't handle errors in any way.