Using RxJS shareReplay to Cache HTTP Requests: Practical Guide
Learn how to cache HTTP GET responses with RxJS shareReplay, configure bufferSize and refCount, avoid memory leaks, and handle stale data in Angular services.
12 Jun 2026, 07:06 UTC

Preventing Duplicate Network Requests
In complex applications, multiple components often need the same piece of data (such as a user profile or a configuration set) simultaneously. Without a caching strategy, each subscription to an HTTP observable triggers a new network request, wasting bandwidth and increasing server load.
The shareReplay() operator solves this by transforming a cold observable (which starts a new execution for every subscriber) into a hot observable. It multicasts the source emission to all current subscribers and stores a specified number of previous values in a buffer to replay to future subscribers.
Implementation and Configuration
To implement caching, place shareReplay() at the end of your data stream pipeline. This ensures that the processed result, rather than the raw request, is cached.
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { shareReplay } from 'rxjs/operators';
@Injectable({ providedIn: 'root' })
export class DataService {
private cache$?: Observable;
constructor(private http: HttpClient) {}
getConfig(): Observable {
if (!this.cache$) {
this.cache$ = this.http.get('/api/config').pipe(
// bufferSize: 1 (keep last value)
// refCount: true (dispose source when no subscribers)
shareReplay({ bufferSize: 1, refCount: true })
);
}
return this.cache$;
}
}
Configuration Breakdown
- bufferSize: Defines how many items the operator should remember. For HTTP requests, a value of 1 is standard, as you only need the most recent response.
- refCount: When set to true, the operator tracks the number of subscribers. If the count drops to zero, it unsubscribes from the source, allowing the cache to be refreshed on the next subscription.
- windowTime: Optional time window (in ms) after which cached values expire. Useful for time‑based cache invalidation.
Common Pitfalls and Limitations
Memory Leaks When refCount Is Omitted
Using shareReplay(1) without refCount: true creates a permanent subscription. If the source observable never completes (e.g., a WebSocket stream), the subscription will stay alive indefinitely, consuming memory. Always pair shareReplay with refCount for non‑terminating streams.
Replay Buffer Size Too Large
If bufferSize or windowTime is omitted, shareReplay replays all past values forever. For long‑lived streams this can grow unbounded and exhaust memory. Specify a reasonable buffer or time limit.
Placement in the Operator Chain
Placing shareReplay before operators that transform the data (e.g., distinctUntilChanged) causes the replay buffer to hold raw values. New subscribers will receive the original emission, not the transformed result. Place shareReplay after all transformations you want cached.
Stale Data Handling
shareReplay provides static caching; it does not automatically know when server‑side data changes. To invalidate the cache you can:
- Reset the cached observable (e.g.,
this.cache$ = undefined;) before the next request. - Use
windowTimeto enforce a time‑based expiration.
Practical Verification Steps
- Insert a
tap()before the HTTP call to log when the network request is made:this.http.get('/api/config').pipe( tap(() => console.log('Network request triggered')), shareReplay({ bufferSize: 1, refCount: true }) ); - Subscribe twice in quick succession. Observe only one “Network request triggered” log.
- Remove
refCount: trueand repeat. After both subscriptions unsubscribe, the log should still appear, indicating the source remains subscribed. - Change
bufferSizeto 2 and replay the same request twice with a delay. Verify only the first request triggers the log; the second receives the cached value. - Set
windowTime: 3000(3 s). Subscribe, wait >3 s, then subscribe again. A new network request should be logged, confirming the cache expired.
Conclusion
Using shareReplay with a proper configuration gives you a lightweight, declarative cache for HTTP requests in RxJS. Remember to:
- Always pair with
refCount: trueunless you intentionally want a permanent subscription. - Keep
bufferSizeto 1 for single‑response streams. - Use
windowTimefor time‑based invalidation when needed. - Place the operator after all transformations you want cached.
Follow these guidelines to avoid common pitfalls and enjoy efficient, shared data streams in your Angular or RxJS‑based applications.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.