Skip to content

Commit

Permalink
feat: respect rate limit headers (#69)
Browse files Browse the repository at this point in the history
* feat: respect rate limit headers

* refactor: move util to utils
  • Loading branch information
braaar authored Dec 14, 2023
1 parent 2b2fd34 commit b85c41a
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 2 deletions.
38 changes: 36 additions & 2 deletions src/abax-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import {
type ListVehiclesResponse,
listVehiclesResponseSchema,
} from './calls/list-vehicles.js';
import { makeQuery, withZod } from './common/utils.js';
import { makeQuery, startOfTheNextMinute, withZod } from './common/utils.js';

export type ApiKeyFunc = () => string | Promise<string>;

Expand Down Expand Up @@ -73,7 +73,13 @@ const apiUrls = {
};

export class AbaxClient {
constructor(private readonly config: AbaxClientConfig) {}
remainingRequests: number;
resetTime: Date;

constructor(private readonly config: AbaxClientConfig) {
this.remainingRequests = 60;
this.resetTime = startOfTheNextMinute();
}

/** Gets paged list of Vehicles. Required scopes: `abax_profile`, `open_api`, `open_api.vehicles`. */
listVehicles(
Expand Down Expand Up @@ -344,6 +350,17 @@ export class AbaxClient {
private async performRequest<R, E>(
call: (apiKey: string) => Promise<CallReturn<R, E>>,
): Promise<R> {
if (this.remainingRequests === 0) {
const now = new Date();

if (now < this.resetTime) {
await new Promise(resolve =>
setTimeout(resolve, this.resetTime.getTime() - now.getTime()),
);
}
this.remainingRequests = 60;
}

const requestCall = async (apiKey: string) => {
const res = await call(apiKey);

Expand Down Expand Up @@ -376,6 +393,23 @@ export class AbaxClient {
'user-agent': this.config.userAgent ?? 'abax-node-sdk/1.0',
Authorization: `Bearer ${apiKey}`,
}))
.parseResponse(response => {
const remainingRequests = response.headers.get(
'X-Ratelimit-Remaining-Minute',
);
if (remainingRequests) {
this.remainingRequests = Number(remainingRequests);
}

const resetTime = response.headers.get('X-Rate-Limit-Reset');

if (resetTime === null) {
// set reset time to start of the next minute
this.resetTime = startOfTheNextMinute();
} else {
this.resetTime = new Date(resetTime);
}
})
.mapError(async error => {
if (error instanceof TypicalHttpError) {
if (error.res.status === 401) {
Expand Down
9 changes: 9 additions & 0 deletions src/common/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { addMinutes, setMilliseconds, setSeconds } from 'date-fns';
import type { z } from 'zod';

export function withZod<T extends z.ZodTypeAny, Output = z.infer<T>>(
Expand Down Expand Up @@ -41,3 +42,11 @@ export function makeBody(data: {

return params;
}

export function startOfTheNextMinute(): Date {
const now = new Date();
addMinutes(now, 1);
setSeconds(now, 0);
setMilliseconds(now, 0);
return now;
}

0 comments on commit b85c41a

Please sign in to comment.