1

I have a web application that need to use sometimes a Token to access my third partner endpoints.

This Token is located on my database, and is obtained in an endpoint at my own server.

I'm trying to build a structure that only request the token on my backend, the first time that some function that needs it is called. And when i already had the token, i'm gonna used it on my local variable.

export class Service {
    private readonly api_url: string = environment.API_URL;
    private token: string;

    getToken(): Observable<any> {
        // HTTP call to get the token on my own backend and return the token
    }

    partnerFirstFunction(): Observable<any> {
        if (!this.token)
            this.getToken().subscribe((response) => {
                this.token = response;

                var headers = new HttpHeaders({ 'Authorization': 'bearer ' + this.token });
                return this.http.request<any>('POST', 'partnerUrl', { headers: headers }).pipe(map(response => {
                    return response;
                }));
            });
        // I WANT TO PREVENT THE ABOVE CODE FROM REPEATING
        else {
            var headers = new HttpHeaders({ 'Authorization': 'bearer ' + this.token });
            return this.http.request<any>('POST', 'partnerUrl', { headers: headers }).pipe(map(response => {
                return response;
            }));
        }
    }

    partnerSecFunction(): Observable<any> {
        if (!this.token)
            this.getToken().subscribe((response) => {
                this.token = response;

                var headers = new HttpHeaders({ 'Authorization': 'bearer ' + this.token });
                return this.http.request<any>('POST', 'partnerUrl', { headers: headers }).pipe(map(response => {
                    return response;
                }));
            });
        // I WANT TO PREVENT THE ABOVE CODE FROM REPEATING
        else {
            var headers = new HttpHeaders({ 'Authorization': 'bearer ' + this.token });
            return this.http.request<any>('PATCH', 'partnerUrl', { headers: headers }).pipe(map(response => {
                return response;
            }));
        }
    }
}

I tried to build a get/set to my token variable, making the "already existing" validation inside the get method, but when needed to request the token, the code continues without waiting the request.

private _token: string;
get token(): string {
    if (this._token == null)
        this.getToken().subscribe((response) => {
            return this._token;
        });
    else
        return this._token;

}
Amer
  • 6,162
  • 2
  • 8
  • 34
Patrick Freitas
  • 681
  • 1
  • 5
  • 18

2 Answers2

2

You can achieve that using shareReplay operator with your HTTP request, and assigning it to an observable token$, which you can use to get the token then switch/merge map the result to the other requests.

You can try the following:

export class Service {
  token$: Observable<string>;

  constructor() {
    // shareReplay will share the result of the request for all subscribers
    // replace of('NEW_TOKEN') with your http request.
    this.token$ = of('NEW_TOKEN').pipe(shareReplay(1));
  }

  partnerFirstFunction(): Observable<any> {
    return this.token$.pipe(
      switchMap(token => {
        var headers = new HttpHeaders({
          Authorization: 'bearer ' + token
        });
        return this.http
          .request<any>('POST', 'partnerUrl', { headers: headers })
          .pipe(
            map(response => {
              return response;
            })
          );
      })
    );
  }

  partnerSecFunction(): Observable<any> {
    return this.token$.pipe(
      switchMap(token => {
        var headers = new HttpHeaders({
          Authorization: 'bearer ' + token
        });
        return this.http
          .request<any>('PATCH', 'partnerUrl', { headers: headers })
          .pipe(
            map(response => {
              return response;
            })
          );
      })
    );
  }
}
Amer
  • 6,162
  • 2
  • 8
  • 34
  • Seems like that approach is better designed, compared to the one did on https://stackoverflow.com/questions/49605090/how-to-return-value-inside-subscribe-angular-4. Any significant difference between then? – Patrick Freitas Sep 08 '21 at 20:07
  • In the mentioned approach, you have to subscribe to one extra observable, to get the result and store them within the `Subject`, and you have to check if the subject has a value or not before using it (and call the prepare function if not), however, in the approach I mentioned, there is no need to subscribe to any observables before using the `token$` and subscribing to the `partnerFirstFunction ` or `partnerSecFunction` functions, and there is no need to check it before using it. – Amer Sep 09 '21 at 07:32
  • I tried your approach, but when using the `token` on the `HttpHeaders`, the variable is a Observable yet and not a string, so i'm receiving an unauthorized response.. the shareReplay line is `of(this.http.request('GET', 'url')).pipe(shareReplay(1));` – Patrick Freitas Sep 09 '21 at 14:59
  • You should replace `of('NEW_TOKEN')` with the http request, not only the NEW_TOKEN. So you just need to remove `of` from your code above. – Amer Sep 09 '21 at 15:04
-1

Maybe you should use

private static token: string;

So that token field would be initialized once for all instances of Service class