Apply an API Request to Each Entity in a List with Reactive Loading Status Tracking

This article shows how to apply an API request to each entity of a list, display its loading status, and execute requests in parallel.

You'll find an example you can reuse in your applications.

Here is an example:

A data list with items updated and deleted using groupBy RxJs

To make this easily, I will introduce you to the groupBy operator combined with another custom operator I often use, called statedStream.

Create the "statedStream" Operator to Track API Request Loading Status

The statedStream operator helps track the loading status of an asynchronous request. I usually use it during API calls.

Its behavior is similar to Angular's httpResource, but we stay within the world of observables.

updateItem$
  .pipe(
    switchMap((updateItem) => // everytime, updateItem$ emits a new value, it cancels the existing API call and creates a new API call
      statedStream(updateApiCall$(updateItem), updateItem)
    )
  )
  .subscribe((data) => console.log('data', data));

Instead of waiting to receive a single value once the API call is complete, statedStream emits an initial value indicating that the request is loading (isLoading: true).

Logs of statedStream RxJs responses

Here is part of the statedStream code:

export function statedStream<T>(
  toCall: Observable<T>,
  initialValue: T
): Observable<SatedStreamResult<T>> {
  return toCall.pipe(
    map(
      (result) =>
        ({
          isLoading: false,
          isLoaded: true,
          hasError: false,
          error: undefined,
          result,
        } satisfies SatedStreamResult<T>)
    ),
    startWith({
      isLoading: true,
      isLoaded: false,
      hasError: false,
      error: undefined,
      result: initialValue,
    }),
    catchError((error) =>
      of({
        isLoading: false,
        isLoaded: false,
        hasError: true,
        error,
        result: initialValue,
      })
    )
  );
}

Note: If you're not familiar with observable streams, remember that when an API call returns an error, your stream stops listening for further emissions (here, updateItem$).

Thanks to statedStream, errors are caught and handled within the result, allowing the stream to continue emitting new requests.

Here is a Stackblitz link to see this function in detail.

Unlock the Titanic Potential of the groupBy Operator

Have you ever used the groupBy operator from RxJs? Personally, when I first read the documentation, I didn't get it. The tenth time... still no. But thanks to this example, I finally understood!

You can also check the documentation and an example on Stackblitz.

Basically, we reuse the statedStream example and add it into the groupBy stream:

updateItem$
  .pipe(
    groupBy((updateItem) => updateItem.id), // create a group for each unique id
    mergeMap((group$) => {
      console.log('group$', group$.key);
      return group$.pipe(
        switchMap((updateItem) =>
          statedStream(updateApiCall$(updateItem), updateItem)
        )
      );
    })
  )
  .subscribe((data) => console.log('Received:', data));

Then, we emit some updates and you'll see how it works:

console.log("emit updateItem first time", 'id: 4')
updateItem$.next({
  id: '4',
  name: 'Romain Geffrault 4',
});

console.log("emit updateItem first time", 'id: 5')
updateItem$.next({
  id: '5',
  name: 'Romain Geffrault 5',
});

setTimeout(() => {
  console.log("emit updateItem second time", 'id: 4')
  updateItem$.next({
    id: '4',
    name: 'Romain Geffrault 4, updated twice',
  });
}, 5000)

Result:

Logs of statedStream and groupBy RxJs

Thanks to groupBy, it's simple to launch multiple API requests in parallel.

Here’s the link to see it in action.

In this example, I grouped by ID, which is a basic case, but you can push the concept further.

Display a List of Entities with Reactive Loading Status in Angular

Reactive Data List with Detailed Entity Status Tracking with groupBy RxJs Angular

Let’s be pragmatic: here's an implementation close to a real-world case that you can reuse easily.

Managing Entity Status in Reactive Data Lists with RxJS

Unfortunately, the Stackblitz link doesn't work, but here’s the GitHub repo you can clone to try it out.

I used NodeJs v20.

npm i
ng serve

The pages you should check are:

I added many comments to explain how some RxJs functions work if you're not used to them.

I used a declarative/reactive approach here, which is my preferred way due to its many advantages.

You’ll notice I managed cases where API calls finish before unsubscribing the streams (thus preventing memory leaks and weird behaviors).

I love this example, but it can still be improved.

For instance, I had to repeat the data types multiple times for TypeScript.
Even though it's not that hard to add, I didn't handle keeping the existing list displayed during navigation, nor adding selectors easily...

Another point: despite everything, all these code snippets take up space and hurt the overall component visibility.

A solution could be to split the different cases inside the scan into separate functions, similar to reducers. But it might be a bit tricky to properly infer types without help.

We could also imagine applying a bulk action to several items at once (bulkEdit, etc.).

I've taken all these points into account and am currently creating a small experimental tool that will allow me to implement these mechanisms declaratively.

It’s somewhat similar to a server-state management tool, like TanStackQuery. It still needs some thinking, but I can’t wait to show you the result.

If you have any questions or would like to discuss it, feel free to comment — I’ll be happy to answer as best as I can.

P.S.: I didn’t use signals because:

  1. I want to apply this pattern in apps that aren't yet using the latest Angular versions.
  2. You can easily convert to a signal if needed.
  3. More importantly, the updates/deletes are triggered by events, not states — which observables handle perfectly but signals don't.