This content originally appeared on DEV Community and was authored by Xhani Manolis Trungu
If you’ve been working with Angular long enough, you’ve probably met HTTP interceptors. They’re like little bouncers standing at the door of every HTTP request:
Need an auth token? They’ll attach it.
Want to log everything? They’ll spam your console.
Craving retries and caching? They’ll hook you up.
But here’s the catch: not all requests need retries or caching.
A login request should never retry automatically (unless you really want to lock yourself out after 3 failed attempts).
An analytics API probably doesn’t need caching (who cares how many clicks you had yesterday?).
A product catalog API? That’s a perfect candidate for retries and caching — because your users expect snappy results when browsing products.
This is where conditional interceptors shine. Instead of blindly applying logic everywhere, we tell Angular:
“Only do this for specific APIs. Don’t waste my time elsewhere.”
The Goal
Here’s what we’re aiming for:
Apply retry logic only to specific APIs.
Cache results from APIs that benefit from re-use.
Keep the pipeline clean by skipping unnecessary logic.
Sounds good? Let’s code.
Retry Interceptor (Conditional)
Retries are great for flaky networks — but disastrous for sensitive endpoints like login. Let’s apply retries only to our catalog API.
import { HttpInterceptorFn } from '@angular/common/http';
import { retry } from 'rxjs';
export const retryInterceptor: HttpInterceptorFn = (req, next) => {
const shouldRetry = req.url.includes('/api/catalog');
if (shouldRetry) {
return next(req).pipe(
retry(3) // 🔄 retry failed requests up to 3 times
);
}
return next(req); // 🚫 no retry for other endpoints
};
Here we only retry /api/catalog requests. Login, auth, and other APIs are left untouched (your account is safe
).
Simple Caching Interceptor (Conditional)
Nobody likes waiting for the same data twice. Let’s add a simple in-memory cache, again only for /api/catalog.
import { HttpInterceptorFn } from '@angular/common/http';
import { of, tap } from 'rxjs';
const cache = new Map<string, any>();
export const cachingInterceptor: HttpInterceptorFn = (req, next) => {
const isCacheable = req.method === 'GET' && req.url.includes('/api/catalog');
if (isCacheable) {
const cachedResponse = cache.get(req.url);
if (cachedResponse) {
console.log('📦 Returning cached response for', req.url);
return of(cachedResponse); // ✅ serve from cache
}
return next(req).pipe(
tap((event) => {
cache.set(req.url, event); // 💾 store in cache
})
);
}
return next(req); // 🚫 no caching for other requests
};
This way, repeated GET /api/catalog/… requests are blazing fast. Other endpoints? Business as usual.
Conditional Composition
Now let’s combine caching + retry into a single conditional interceptor.
import { HttpInterceptorFn } from '@angular/common/http';
import { cachingInterceptor } from './caching.interceptor';
import { retryInterceptor } from './retry.interceptor';
export const conditionalApiInterceptor: HttpInterceptorFn = (req, next) => {
const isCatalogApi = req.url.includes('/api/catalog');
if (isCatalogApi) {
// 🌀 Compose caching + retry
return cachingInterceptor(req, (cachedReq) =>
retryInterceptor(cachedReq, next)
);
}
// 🚫 Fallback: no special logic
return next(req);
};
Magic!
Catalog requests get both caching & retry.
Other requests cruise straight through untouched.
Registering the Interceptors
Finally, let’s wire everything up in Angular 20.
import { bootstrapApplication } from '@angular/platform-browser';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { AppComponent } from './app/app.component';
import { conditionalApiInterceptor } from './app/interceptors/conditional-api.interceptor';
bootstrapApplication(AppComponent, {
providers: [
provideHttpClient(
withInterceptors([conditionalApiInterceptor])
),
],
});
That’s it. Your app now has context-aware retries and caching.
Benefits
Granular control – retries/caching only where they’re safe.
Performance boost – no more wasted network calls.
Cleaner code – no bloated if statements everywhere.
Real-World Use Cases
Retry catalog/product listing APIs but never login requests.
Cache user profile data for smoother navigation.
Apply special logic only in production (keep dev logs noisy).
Takeaway
Conditional interceptors in Angular 20 make your HTTP pipeline smart, fast, and drama-free. Instead of spamming all requests with retries or caching, you only enhance the ones that truly matter.
This pattern keeps your code clean, reliable, and ready to scale.
Bonus: You can even pair this with feature flags to toggle retries/caching at runtime — no redeploys needed.
This content originally appeared on DEV Community and was authored by Xhani Manolis Trungu