I am trying to check the validity of a function I have written in Typescript, in congruence with RxJS observables, that fetches some bookings from one service and then for each booking fetches its corresponding location and activity from another service.
I am simply writing this post to verify the validity of what I have written and to ask if there is anything I could have done more efficiently.
let params = new HttpParams();
params = params.append('status', 'C');
params = params.append('offset', offset.toString());
params = params.append('limit', limit.toString());
return this.http.get(`${this.environment.booking.url}/my/bookings`, { params }).pipe(
mergeMap((bookings: Booking[]) => {
if(bookings.length > 0) {
return forkJoin(
bookings.map((booking: Booking) =>
forkJoin(
of(booking),
this.activityService.getActivity(booking.activity),
this.locationService.getLocation(booking.finalLocation),
).pipe(
map((data: [ Booking, Activity, Location ]) => {
let booking = data[0];
booking.activityData = data[1];
booking.finalLocationData = data[2];
return booking;
})
)
)
)
}
return of([]);
}),
catchError((err: HttpErrorResponse) => throwError(err))
);
I am expecting for this function to return a list of bookings alongside their corresponding location and activity. However more importantly I want to verify that what I am doing is correct and sensible. Is there anything I could have done differently to make it cleaner/ more human-readable (not nit-picking, please 😁 )?
On a different note, that of performance, I also have a follow-up question with regards to performance. Given that a list of bookings has common activities and locations. Is there a way to only fetch activities and locations without any duplicate HTTP requests? Is this already handled under the hood by RxJS? Is there anything I could have done to make this function more efficient?
I'm not sure about the efficiency, but, at least for me, it was a little hard to read
Here's how I'd do it:
I used a dummy API, but I think it correlates with your situation
const usersUrl = 'https://jsonplaceholder.typicode.com/users';
const todosUrl = 'https://jsonplaceholder.typicode.com/todos';
const userIds$ = of([1, 2, 3]); // Bookings' equivalent
userIds$
.pipe(
filter(ids => ids.length !== 0),
// Flatten the array so we can avoid another nesting level
mergeMap(ids => from(ids)),
// `concatMap` - the order matters!
concatMap(
id => forkJoin(ajax(`${usersUrl}/${id}`), ajax(`${todosUrl}/${id}`))
.pipe(
map(([user, todo]) => ({ id, user: user.response, todo: todo.response }))
)
),
toArray()
)
.subscribe(console.log)
With this in mind, here is how I'd adapt it to your problem:
this.http.get(`${this.environment.booking.url}/my/bookings`, { params }).pipe(
filter(bookings => bookings.length !== 0),
// Get each booking individually
mergeMap(bookings => from(bookings)),
concatMap(
b => forkJoin(
this.activityService.getActivity(b.activity),
this.locationService.getLocation(b.finalLocation),
)
.pipe(
map(([activity, location]) => ({ ...b, activity, location }))
)
),
// Getting the bookings array again
toArray()
catchError((err: HttpErrorResponse) => throwError(err))
);