Search code examples
angularfirebasepromiseobservablesubscription

Error chaining from a service to component for a http.post subscription


I am using firebase to authenticate a sign in and then send a request to my backend to create a custom session token for the logged in user. For this, I send in a function call from my component.ts to service.ts.



  SignIn(email: string, password: string) {
    return this.angularFireAuth
      .signInWithEmailAndPassword(email, password)
      .then(async res => {
        const idToken = await res.user.getIdToken()
        this.http.post(environment.baseURL + "auth/sessionLogin", { idToken: idToken }).pipe(
          catchError(err=>{
            return throwError("FailedLogin")   // ** NOT USEFUL ERROR 
          })
        )
        .subscribe((response) => {
          this.router.navigate(["/signup"]);    // ** IF SIGN IN IS SUCCESSFUL
        }
        // , (err) => {
        //   throw new Error("Failed Login").   // ** NOT USEFUL ERROR
        // }
      )
        return { loggedIn: false, message: "Failed Login" }   // ** INCASE MY BACKEND THROWS ERROR

      }).catch(err => {
        return { loggedIn: false, message: err.message }.  // ** INCASE FIREBASE THROWS ERROR

      });
  }

The component.ts code is

  async signIn(loginF: NgForm) {
    const email = loginF.value.email
    const password = loginF.value.password
    const response = await this.authenticationService.SignIn(email, password)
    if(!response.loggedIn){
      // my error handling code for flash messages
    }

  }

Now, the above code does the job, but I know that it's one of the worst ways of error handling as I am not using the observables to their fullest potential. I want to know how to use the catchError or perhaps try-catch blocks to not manually send and check a custom { loggedIn: false, message: err.message } like object to my component.

The catchErrors above are basically useless as they are not being forwarded to my component. If I return the this.http.post.., it says it is a subscription and I can't do a .then() on it + it says the response in my frontend would then either be of the type Subscription or of the type { loggedIn: false, message: err.message }


Solution

  • UPDATE : A POSSIBLE ANSWER

    I have broken down the two things : signing in using firebase and then sending a request to backend for token into two different parts. It looks like a much more elegant solution for it doesn't involve any manual response object creation.

    ** component.ts**
      async signIn(loginF: NgForm) {
        const email = loginF.value.email
        const password = loginF.value.password
        let idToken
        try{
          idToken = await this.authenticationService.SignIn(email, password)
        }catch(err){
          idToken = null;         // ** INCASE FIREBASE FAILS
          // error handling for flash messages
        }
    
    
        if(idToken!= null){
          this.authenticationService.SignInBackend(idToken).subscribe((response) => {
            this.router.navigate(["/signup"]);
          }
          , (err) => {
            // error handling for flash messages // ** INCASE BACKEND FAILS
          }
        )
        }
    
      }
    
    ** service.ts**
    SignIn(email: string, password: string) {
        return this.angularFireAuth
          .signInWithEmailAndPassword(email, password)
          .then(async res => {
            const idToken = await res.user.getIdToken()
            return idToken;
          }).catch(err => {
            throw new Error(err.message).      // ** ERROR GETS CHAINED
          });
      }
    
      SignInBackend(idToken){
        return this.http.post(environment.baseURL + "auth/sessionLogin", { idToken: idToken })
    
      }