All insights

Performance

How RxJS Reduced Our Angular API Calls by 40%

One enterprise Angular screen requested the same reference data five times. A shared RxJS caching layer removed the duplication without rewriting the consuming components.

Angular panels sharing one cached RxJS stream instead of making duplicate HTTP API requests.

The original complaint on the Fiji immigration internal case-management system was simple: case pages felt slow. The initial assumption was that rendering or bundle size was responsible.

The network tab showed something different. Several stable reference endpoints — including country lists and workflow categories — were requested five or six times during one navigation.

Why duplicate Angular API requests happen

Multiple Angular panels independently requesting identical reference API data.

Each panel had been designed to be self-contained. It requested the data it needed during initialization instead of assuming that a parent component had already loaded it.

That is a reasonable component design in isolation. The problem appears when several independently reusable components need the same stable reference data.

A harmless request pattern becomes expensive when several components repeat it on the same screen.
export class DocumentChecklistComponent implements OnInit {
  countries: Country[] = [];
  constructor( private readonly api: ReferenceApi ) {} ngOnInit(): void {
    this.api .getCountries() .subscribe((countries) => {
      this.countries = countries;
    });
  }
}

Use RxJS shareReplay to share one HTTP request

Multiple RxJS subscribers sharing one Angular HTTP request through shareReplay.

Moving every request into the parent would reduce duplication, but it would also make reusable panels dependent on a specific screen structure. A shared service can solve the request problem without changing those component boundaries.

shareReplay allows every subscriber to reuse the same Angular HTTP result.
@Injectable({ providedIn: 'root' }) export class ReferenceApi {
  private readonly countries$ = this.http .get<Country[]>( '/api/reference/countries' ) .pipe( shareReplay({
    bufferSize: 1, refCount: false,
  }) );
  constructor( private readonly http: HttpClient ) {} getCountries(): Observable<Country[]> {
    return this.countries$;
  }
}

Multiple subscribers now share one HTTP request. Because the stream is created once at service level, returning to the screen can reuse the previously emitted value instead of automatically starting another request.

RxJS caching is easy; cache invalidation is harder

Event-driven RxJS cache invalidation clearing Angular reference data before the next request.

Caching a value is only correct while that value remains valid. A country list may be effectively static for a session, while workflow categories or office locations may change after an administrative action.

Instead of choosing an arbitrary timeout, invalidate the cache when an event occurs that can actually make the cached value stale.

Invalidate cached RxJS data when the underlying resource changes.
private countriesCache$?: Observable<Country[]>;
getCountries(): Observable<Country[]> {
  this.countriesCache$ ??= this.http .get<Country[]>( '/api/reference/countries' ) .pipe( shareReplay({
    bufferSize: 1, refCount: false,
  }) );
  return this.countriesCache$;
}
invalidateCountries(): void {
  this.countriesCache$ = undefined;
}

The performance impact of removing duplicate requests

Across the case workflow, API consumption dropped by approximately 40%, while frontend load behaviour improved substantially. The 40% is measured against the pre-optimisation frontend data flow of that workflow and is an approximation, not an audited benchmark. The figure worth checking on your own screen is the number of identical requests per navigation: five or six here, before the change. The important point is that the improvement came from changing the data layer rather than rewriting component rendering logic.

This is why the network tab is one of the first places I look when an Angular screen feels slow. Sort requests by URL and count duplicates before assuming the problem is rendering, change detection or the backend.

The RxJS lesson worth keeping

A duplicated HTTP request is often not a mistake in the component where it appears. It is evidence that several reasonable local decisions are composing into an expensive global behaviour.

Shared RxJS streams are useful because they let the data layer solve that composition problem while consumers keep simple boundaries. The important work is deciding what should be shared, how long it remains valid and what event makes it stale.

Sources

Where this is applied

Keep reading