Managing API call limits using RxJs

Currently, I am in the process of developing an Angular application that utilizes an API provided by a social network. Unfortunately, this API has a restriction of only allowing 5 API calls per second.

The typical solution to this limitation involves implementing custom logic to track and queue requests based on the restrictions. If a 6th request is made within one second, it will be delayed until the next allowable time slot.

However, I am interested in exploring more elegant solutions using RxJs.

For example, I have considered setting the debounceTime for the Observable, as shown in the code snippet below. Yet, this approach presents limitations, as it prevents making multiple requests with intervals shorter than 200ms between them.

this.searchControl.valueChanges
    .debounceTime(200) // 200ms ~ 5 requests per second 
    .switchMap(search => this.api.searchPeople(search))

Are there any techniques within RxJs that can help restrict the number of emits per interval and queue excess requests when they are being sent too frequently?

Answer №1

If you're looking for a different approach with Angular, check out this custom interceptor built for Angular 8 and RxJS 6:

@Injectable()
export class RateLimitInterceptor implements HttpInterceptor {

  private readonly timeInterval = 1000 /* 1 second interval */;

  private availableRequests = 10 /* 10 requests allowed per interval */;

  private requestLimit$ = new BehaviorSubject(this.availableRequests);

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return this.requestLimit$.pipe(
      filter(() => this.availableRequests > 0),
      take(1),
      tap(() => this.decrementRequests()),
      tap(() => timer(this.timeInterval).subscribe(() => this.incrementRequests())),
      mergeMap(() => next.handle(req))
    );
  }

  private incrementRequests(): void {
    this.adjustRequestLimit(1);
  }

  private decrementRequests(): void {
    this.adjustRequestLimit(-1);
  }

  private adjustRequestLimit(value: number): void {
    this.requestLimit$.next(this.availableThreads += value);
  }
}

Answer №2

If you want a way to monitor the frequency of your recent API calls, you could implement a system where each call consumes a token and these tokens are renewed every second. Below is an operator that can assist with this:

Observable.prototype.rateLimit = function (count: number, slidingWindowTime: number, scheduler = async) {
  let tokens = count;
  const tokenChanged = new BehaviorSubject(tokens);
  const consumeToken = () => tokenChanged.next(--tokens);
  const renewToken = () => tokenChanged.next(++tokens);
  const availableTokens = tokenChanged.filter(() => tokens > 0);

  return this.mergeMap(value =>
    availableTokens
    .take(1)
    .map(() => {
      consumeToken();
      Observable.timer(slidingWindowTime, scheduler).subscribe(renewToken);
      return value;
    }));
}

declare module 'rxjs/Observable' {
  interface Observable < T > {
    rateLimit(count: number, slidingWindowTime: number, scheduler ? : Scheduler): Observable < T >
  }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Mastering regular expressions in TypeScript

My goal is to perform linting on staged files that are either .ts or .tsx and located within the src folder. I am aware that for selecting all js files one can use "*.js": [--list of commands--] inside the lint staged property. I'm curious to learn m ...

Tips for obtaining response headers

Currently, I am utilizing Angular version 15.0 and retrieving a list of items from the backend (ASP.NET Core 5) with an additional item attached to the header. The GET method in the client-side service is as follows: /** GET Paged commodities from the s ...

What goes on behind the curtain when moving between components in an Angular and Node.js single-page application (SPA)?

I'm struggling to comprehend how the amalgamation of Angular and Node.js can result in a Single Page Application website. My inquiry will be more comprehensible with an illustration: Let's assume I am attempting to construct a SPA website: On t ...

Count of items in filtered data displayed using Angular 5 *ngFor

Looking for help with my ngFor loop: *ngFor="let item of items | filterBy: ['SN', 'Name']: userFilter | orderBy: order | paginate: {itemsPerPage: 10, currentPage: 1}; let i = index;" Trying to retrieve the count of filtered items. Whe ...

Issue with Angular 8: Undefined value for ngModuleType.ngModuleDef.id when trying to import NguCarousel module

After attempting to import the NguCarousel module, my application encounters an issue due to ngModuleType.ngModuleDef being undefined. Interestingly, if I disable the import of the NguCarousel module, the app functions properly. I have experimented with di ...

Encountering an issue with resolving parameters for the DecimalPipe in ngBootstrap/Angular2

Just delving into the world of Angular2 and trying to learn through hands-on experience. However, I've hit a roadblock! I attempted to import ng-bootstrap and encountered this error: https://i.stack.imgur.com/QDVJ3.png Here's my systemjs.config ...

How to change Input settings in Angular 6?

I'm looking to toggle between options in a select field and display another input depending on my selection. Ownership: <select ngModel="selectedValue"> <option value="true">Owned</option> <option value="false">Not Owned< ...

Is there a way to merge TypeScript code with C++ during compilation?

Currently, I have a project written entirely in C++. However, there is an additional file written in typescript because I couldn't find equivalent libraries in C++. This typescript file performs the following actions: It contains a typescript CLI cod ...

Adding the project license to the build in an Angular CLI project for xRay license scanning: A step-by-step guide

Our project has a unique licensing agreement specified in the license attribute within package.json. Upon running ng build, we notice that a 3rdpartylicenses.txt file is created in the dist folder containing licenses for all dependencies except our custom ...

The best location for storing Typescript files within an ASP.NET Core project

My Typescript app is built on AngularJS 2 with ASP.NET Core, and currently I store my TS files in the wwwroot directory. While this setup works well during development, I am concerned about how it will function in production. I aim to deploy only minified ...

Navigating Angular: Uncover the Secrets of Unwrapping Json Data

I need some help with parsing Json data in Angular TypeScript. The data is structured as follows: https://i.sstatic.net/H7s8q.png When calling a REST API, I want to convert a Java class object into an array of model classes. How can this be achieved? "g ...

What techniques can I use to modify an object while it's being iterated through?

I've been attempting to manipulate the object while looping through it, but unfortunately, it's not working. How can I modify it so that the constant patient includes the property lastActivity inside the this.model array? My code looks like this ...

After the introduction of ReactiveFormsModule, the functionality of the Angular router has ceased

I am working on setting up a reactive form in Angular for a login page. Here is my login form: <form [formGroup]="loginForm" (ngSubmit)="login(loginForm.value)"> <div class="form-group"> <label for="username">Username</label> ...

Angular resolver does not return anything if the outcome is an observable generated using the .asObservable() method

While working on my project, I wanted to simulate my http calls by using a BehaviorSubject along with Observable properties in my resolver service. However, I faced an issue and I am not sure why this code snippet is not functioning as expected: schedule- ...

flickering effect problem

What could be causing the issue where [@fadeInOut] only works on the initial page load when toggling isExpanded to hide or show content? Due to [@fadeInOut], the content stops showing up. Any thoughts on what might be causing this and any alternative solut ...

What is the process for removing a particular file from my bundle?

I am currently utilizing webpack to build my angular2/typescript application and have successfully generated two files, one for my code and another for vendors. However, I am in need of a third file to separate my config (specifically for API_ENDPOINT) whi ...

Bringing @angular/code into a directory that is not within an Angular project

Currently, I have an Angular 2 project folder with a separate service folder named "auth.service.ts" located outside of it. Within this service file, I am importing `Injectable` from `@angular/core`. However, due to the service being located outside of t ...

Utilizing the return value of a MockService in a Jasmine unit test results in test failure

A StackBlitz has been set up with the karma/jasmine loader for you to view test pass/fail results. The application is functioning correctly. All my tests are expected to pass without any issues, but I am encountering an unusual error when using a mockser ...

React - Incorrect use of hooks and class components

Understanding hooks as the opposite of a class component can be confusing. A simple line of code can trigger an error when trying to adapt it for use in a class component. Take, for example, this situation: .jsx files and functional components work seamles ...

django-cors-headers failed to function properly on Django version 1.6.11

Currently utilizing Angular4 in conjunction with django-nonrel as the backend. Initially encountered a CORS error and resolved it by installing django-cors-headers to enable CORS support for Django. Due to django-nonrel's compatibility limitations, ha ...