Caching Angular HTTP Requests with RxJS shareReplay
Learn how to wrap an HttpClient GET observable with shareReplay(1) so multiple components trigger only one network call, and understand the pitfalls of refCount and error handling.
23 Jul 2026, 17:07 UTC

Problem: Duplicate HTTP calls waste bandwidth and slow UI
\nWhen several Angular components inject the same service and subscribe to a method that returns this.http.get(url), each subscription triggers a separate XHR. This can overload the server and cause inconsistent data if the request is not idempotent.
Takeaway: Wrap the observable with shareReplay(1)
\nPlacing shareReplay(1) after the HTTP call creates a hot observable that caches the most recent emission. New subscribers receive the cached value instantly, and the source HTTP request is executed only once.
Worked example: Angular service
\nimport { HttpClient } from '@angular/common/http';\nimport { Injectable } from '@angular/core';\nimport { shareReplay } from 'rxjs/operators';\n\n@Injectable({ providedIn: 'root' })\nexport class UserService {\n private readonly apiUrl = '/api/users';\n\n constructor(private http: HttpClient) {}\n\n /** Returns a cached observable of the user list */\n getUsers() {\n return this.http.get<any[]>(this.apiUrl).pipe(\n shareReplay(1) // buffer size 1, refCount defaults to true\n );\n }\n}\n\nTwo components that call userService.getUsers().subscribe(...) will see only one network request in the DevTools Network tab, and both will receive the same payload.
How shareReplay works
\nThe operator internally keeps a subject that stores the latest value(s) from the source observable. When the first subscriber arrives, it subscribes to the source (triggering the HTTP request). Subsequent subscribers are subscribed to the subject, which immediately replays the buffered value. If all subscribers unsubscribe and refCount is true (the default), the internal subscription to the source is disposed, freeing any underlying resources.
Configuration options
\n- \n
shareReplay(bufferSize)– number of emissions to replay. Use1for the most recent value. \n shareReplay(bufferSize, windowTime)– optionally time‑based window. \n shareReplay(bufferSize, refCount)– explicit control over whether the source subscription stays alive after all observers leave. \n
Limits and common mistakes
\n1. Keeping the HTTP request alive unintentionally
\nIf you omit the refCount argument or set it to false, the internal subscription to the source never unsubscribes, even after all components have detached. This can leave a pending XHR that never completes, causing a memory leak.
Fix: Either rely on the default (refCount: true) or explicitly pass true:
shareReplay(1, true)\n2. Caching errors
\nshareReplay replays whatever the source emits, including error notifications. An error from the HTTP call will be cached and re‑emitted to every future subscriber, which may show stale error messages.
\nMitigation: Handle errors upstream (e.g., with catchError) or reset the stream when appropriate:
getUsers() {\n return this.http.get<any[]>(this.apiUrl).pipe(\n catchError(err => {\n // optionally log, then return a fallback or re‑throw\n return throwError(() => err);\n }),\n shareReplay(1)\n );\n}\n\n3. Stale data after source completion
\nIf the HTTP observable completes (which it does after a successful response) and you later subscribe, shareReplay will still emit the last value. This is usually desired for cached data, but if you expect the request to be refreshed periodically, you need to reset the cache.
\nPattern: Combine with takeUntil driven by a refresh signal, or manually re‑create the service method on a timer.
Verification steps
\n- \n
- Open Chrome DevTools → Network tab, preserve log. \n
- Navigate to a route that loads two components injecting
UserService. \n - Observe that only one request to
/api/usersappears. \n - Check that both components receive identical data in their subscriptions. \n
- Unsubscribe both components (e.g., by navigating away) and confirm that the DevTools shows the request completed and no lingering pending requests. \n
When not to use shareReplay
\nAvoid shareReplay for requests that must always hit the server (e.g., submitting a form) or when you need per‑call parameters that vary per subscriber. In those cases, keep the observable cold and let each subscription trigger its own request.
\nBy applying shareReplay(1) judiciously, you reduce redundant network traffic, improve response times, and keep your Angular application’s data layer predictable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.