Is there an RXJS/Angular service pattern to efficiently re-fetch a POST (not a GET), alerting any number of consumers

68 views Asked by At

In Angular+TS, using RXJS observable patterns, and wanting services to avoid explicit subscriptions or opinionated piping operators.

Say that we have a file appointments-api.service.ts, with functions exposed that almost always take this shape:

public list(requestBody: AppointmentListRequest) {
    return this.apiService.post<
      AppointmentListRequest,
      AppointmentListResponse
    >(listUrl, requestBody);
}

Most of our endpoints are required to be a POST (not particularly RESTful, sorry). The apiService.post method looks as follows:

post<TRequest, TResponse>(
    url: string,
    requestBody: TRequest,
    responseType: string = 'json'
  ): Observable<HttpState<TResponse>> {
    return this.http
      .post<TResponse>(this.buildUrl(url), requestBody, {
        responseType: responseType as 'json',
      })
      .pipe(
        retry(1),
        map(data => ({ isLoading: false, data })),
        catchError(error => of({ isLoading: false, error })),
        startWith({ isLoading: true }),
        shareReplay()
      );
  }

And finally, we have two or more components with different arguments to the appointment list() call. Under certain conditions, like when an appointment is cancelled, we want all calls to list() in any component to re-evaluate, similar to how react-query would handle query invalidations:

export class AComponent {
  appointments$ = this.appointmentsApi.list({pageNumber: 0, pageSize: 3}) // Sub managed with async pipe

  onCancel() {
    this.appointmentsApi.refetchList(); // Hoping the API can look similar to this
  }
}

export class BComponent {
  appointments$ = this.appointmentsApi.list({pageNumber: 1, pageSize: 15}) // Sub managed with async pipe

  onCancel() {
    this.appointmentsApi.refetchList(); // Hoping the API can look similar to this
  }
}

The core question: how can we write a refetchList() function in the service that safely pushes a refetch, and only for current consumers of list() ? I have read lots and lots about how behavior subjects can help here, and I am aware of the concept. However, because list() is a function that creates new observables for each parameterized call, and is not an observable property directly on the service itself, the use of a behavior subject to trigger a refetch will fire one new API call for every single call to list() that was ever done. While all the real subscribers get what they need, one can imagine that this is not a scalable solution

Any pointers, explicit or just directionally, are so appreciated!

Tried: using BehaviorSubject(s) in the service, to pipe into a new API call for .list()

Expected: all current subscribers to .list() to fire off just one more time

Got: this, but also one API call for every single time that .list() was called from the service, since the service retains memory of its behavior subject as piped into each .list() call for a new observable

0

There are 0 answers