2

I need to sequentially call multiple HTTP requests. Currently I have this huge epic where I pass all API requests and then try to fire them one by one using mergeMaps. It works, but I believe there must be some easier or cleaner way but. All I found for RxJS was forkJoin but it fires everything in parallel. Am I nitpicking here or is there some much smarter way? Any tips could be helpful here.

Pseudo-code:

All requests are similar and look like this:

import { ajax } from "rxjs/ajax";
import { map } from "rxjs/operators";

export const unpublishAbcApi = (id) =>
  ajax({
    method: "PATCH",
    url: ...,
    headers: ..,
  }).pipe(
    map(({ response }) => ({
      published: response.published,
    }))
  );

And this is my epic

export const deleteEverything = (action$, _,) =>
  action$.pipe(
    actionOfType(delete.request),
    mergeMap(({ payload: { id }}) => 
      unpublishAbcApi(id).pipe(
        mergeMap(() =>
          deleteDefApi(id).pipe(
            mergeMap(() =>
              deleteGhiApi(otherId).pipe(
                mergeMap(() =>
                  deleteJklApi(id).pipe(
                    map(() => ({ id }))
                  )
                )
              )
            )
          )
        )
      );
    ),
    mergeMap(({ id }) => 
      // ...get the id and dispatchSuccess etc.
    ),
    catchError(error => /* error handling */)
  );
bdobry
  • 190
  • 1
  • 8

1 Answers1

1

Since the apis are not interdependent on each other, you can just use concat which will execute APIs in sequence!

export const deleteEverything = (action$, _) =>
  action$.pipe(
    actionOfType(delete.request),
    switchMap(({ payload: { id }}) => concat(
      unpublishAbcApi(id), 
      deleteDefApi(id), 
      deleteGhiApi(otherId), 
      deleteJklApi(id)
      ).pipe(map() => ({id}))),
    mergeMap(({ id }) => 
      // ...get the id and dispatchSuccess etc.
    ),
    catchError(error => /* error handling */)
  );
Naren Murali
  • 19,250
  • 3
  • 27
  • 54
  • Great, thanks! I knew there has to be a cleaner way. A "bonus" question I didn't think of earlier, **what if one of the calls is optional based on the `payload` from the previous Observable?** – bdobry Dec 08 '22 at 09:50
  • 1
    @bdobry For that one set of calls, you need to make the first call, and inside that pipe it and call the second one, deleteDefApi(id).pipe(switchMap((otherId) => deleteGhiApi(otherId))), the rest of the APIs can follow the same pattern! , – Naren Murali Dec 08 '22 at 10:01
  • It didn't work in the end. Returns 4 alerts and a success, can't figure out why – bdobry Dec 08 '22 at 11:06
  • @bdobry Can you provide details of the errors? also the latest code you tried? – Naren Murali Dec 08 '22 at 11:11
  • 1
    It's probably because `concat` will send ALL the intermediate results in the final stream, which is not what you want. What I would do is a chain of `mergeMap`s - emphasis on *chained*, not *nested*; less concise than this solution, but probably more correct. – Nikos Paraskevopoulos Dec 08 '22 at 13:47