I have the following code where I need to get a value from two separate observables (they are actually promises but have been converted to observables using "from") and then use the values returned from each to make a final API call where both the values are used.
The method should return the result from the API call which is an object called "AppUser".
My code also has some logic in it to retry from a backup API if the primary API fails and then if the backup fails it should return the value from local storage.
This code does work but having read up about nested observables not being good practice I wondered if there is a better way to write this? Any advice would be appreciated.
// Get app user
getAppUser(getFromLocal: boolean = false, isRetry: boolean = false): Observable<AppUser> {
// If "get from local" is true
if (getFromLocal) {
return this.getAppUserFromLocal();
} else {
// Create observables to get values from local storage
const customerObs = from(this.authLocal.getCustomerId());
const authTokenObs = from(this.authLocal.getAuthToken());
return customerObs
.pipe(mergeMap((customerId) => {
return authTokenObs
.pipe(mergeMap((authToken) => {
// API Header
const headers = new HttpHeaders({
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${authToken}`
});
const url = (!isRetry ? this.baseUrl : this.backupBaseUrl) + '/GetAppUser?customerid=' + customerId;
return this.http.get(url, { headers })
.pipe(map((appUser: AppUser) => {
// Set AppUser in local storage
this.storage.set('appUser', appUser);
return appUser;
}),
catchError(e => {
if (!isRetry) {
// Try again using backup API
return this.getAppUser(getFromLocal, true);
} else {
// Try again from local storage
return this.getAppUser(true, true);
}
}));
}));
}));
}
}
UPDATED with potential solution
I have updated as per the answers with the following potential solution:
// Get app user
getAppUser(getFromLocal: boolean = false, isRetry: boolean = false): Observable<AppUser> {
// If "get from local" is true
if (getFromLocal) {
return this.getAppUserFromLocal();
} else {
// Create observables to get values from local storage
const customerId$ = from(this.authLocal.getCustomerId());
const authToken$ = from(this.authLocal.getAuthToken());
return forkJoin([
customerId$,
authToken$
]).pipe(
switchMap(([customerId, authToken]) => {
const headers = new HttpHeaders({
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${authToken}`
});
const url = (!isRetry ? this.baseUrl : this.backupBaseUrl) + '/GetAppUser?customerId=' + customerId;
return this.http.get(url, { headers })
.pipe(
map((appUser: AppUser) => {
appUser.AuthToken = authToken;
return appUser;
}),
catchError(e => {
if (!isRetry) {
// Try again using backup API
return this.getAppUser(getFromLocal, true);
} else {
// Try again from local storage
return this.getAppUser(true, true);
}
}));
}),
tap((appUser: AppUser) => {
// Set AppUser in local storage
this.storage.set('appUser', appUser);
}),
);