Throttling API Calls in Angular Using HTTP Interceptors - by Sas
Throttling API Calls in Angular Using HTTP Interceptors - by Sas
Save
Make sure our DB handle these many + 50% (I randomly picked this number)
more API calls
In this post, I want to try another way to handle this in Angular Single Page
Application framework. How about throttling API calls to 2/3 per user? In this case,
the number of API calls reduces to 20% for the same number of users.
HTTP 1.1
When we use HTTP 1.1, browsers do throttle requests and it depends on the
browser. Google Chrome allow only six simultaneous requests per domain with
HTTP 1.1 protocol. Check the following image.
HTTP 2
When we use HTTP 2, browsers can send more simultaneous requests. Check the
following image.
Upgrade Open in app
Idea
Instead of making 15 API calls simultaneously, how about keeping this number
always ≤ 3? Once we get a response/error from one API, we send another.
Loading State
Make sure that we have a loading state per widget instead of blocking entire page till
we complete 15 API calls.
Debounce?
We can debounce. But we can’t exactly guess how much time it will take to complete
an API call. What if we changed the duration of data that we are fetching from one
month to 5 years? We can’t guarantee that we always keep the simultaneous number
of API calls ≤ 3. Also, what if the API calls are completed a little bit early and we are
keeping the app idle and showing loading state.
Reusable?
How can we solve this problem in such a way that we can re-use this in some other
part of the application? Above mentioned throttling in the components can’t be re-
usable.
HTTP Interceptor?
Can we solve this at HTTP Interceptor level? If we solve this at HTTP Interceptor
Upgrade Open in app
level, we don’t need to bother about communication between components. All
widgets trigger their API calls at a time. HTTP Interceptor will take care of
throttling.
Solution
As mentioned earlier there can be many ways to solve this problem. Think about
this as a POC.
const THROTTLE_LIMIT = 3;
@Injectable()
public intercept(
req: HttpRequest<any>,
next: HttpHandler,
): Observable<HttpEvent<any>> {
if (URL_REGEX.test(req.url)) {
return next.handle(req);
Let’s create a new HTTP Interceptor for throttling API calls, which uses above API
Throttle Service.
@Injectable()
constructor(
) {}
intercept(
req: HttpRequest<any>,
next: HttpHandler,
): Observable<HttpEvent<any>> {
interceptors.push({
provide: HTTP_INTERCEPTORS,
useClass: APIThrottleInterceptor,
multi: true,
});
Let’s keep a counter to track active API calls, their URLs. And also an object to
track observables that we send back to Interceptor if throttle limit is reached.
private activeCount = 0;
observer.error();
delete this.reqObs[url];
if(this.activeCount > 0) {
this.activeCount--;
If THROTTLE_LIMIT is not reached, just send the regular request. Make sure to
handle the response (in both success/failure) and continue pending API calls.
processResponse() is explained in the next step
this.activeCount++;
return next.handle(req).pipe(
tap(evt => {
this.processResponse();
return evt;
}),
catchError(err => {
this.processResponse();
return of(err);
}),
if (this.activeCount > 0) {
this.activeCount--;
if (this.reqURLs.length > 0) {
Upgrade Open in app
const url = this.reqURLs[0];
observer.next('done!');
observer.complete();
this.reqURLs = this.reqURLs.slice(1);
delete this.reqObs[url];
If THROTTLE_LIMIT is reached, just create an observable, store it for later use and
send it back.
this.reqURLs.push(url);
this.reqObs[url] = null;
this.reqObs[url] = ob;
});
return obs.pipe(
concatMap(_ => {
return next.handle(req).pipe(
tap(
),
);
With this, we don’t need to touch any existing components and can add throttling of
API calls using HTTP Interceptors.
What’s Next?
As mentioned earlier, there are many ways to handle this, and this solution may not
suit in all cases. This is a simple POC and needs proper testing, and also need to
check if there are any memory leaks and performance issues.