@seriouslag/httpclient
Version:
Typed wrapper HttpClient for axios
76 lines (67 loc) • 2.79 kB
text/typescript
import { Sleep } from '../utilities/sleep';
import { getIsSuccessfulHttpStatus } from '../utilities/getIsSuccessfulHttpStatus';
import { HttpRequestStrategy } from '../HttpRequestStrategies/HttpRequestStrategy';
import { Request, HttpResponse } from '../Adaptors';
export interface ExponentialBackoffOptions {
/** Determines if the first request will be delayed */
delayFirstRequest?: boolean;
/** The maximum number of retries to attempt, default is 5, set to 0 for indefinite retries */
maxRetryCount?: number;
/** The base delay in milliseconds, will be used to calculate backoff */
baseDelay?: number;
/** The maximum delay to use; set to -1 or undefined for no cap */
maxDelay?: number;
/** The factor that will be used to grow the delay */
factor?: number;
}
/** Retries HTTP requests with a specified backoff strategy until the max retry count. */
export class ExponentialBackoffRequestStrategy implements HttpRequestStrategy {
/** TOO MANY REQUESTS STATUS CODE */
private TOO_MANY_REQUESTS_STATUS = 429;
private delayFirstRequest: boolean;
private maxRetryCount: number;
private baseDelay: number;
private factor: number;
private maxDelay: number;
constructor(private options: ExponentialBackoffOptions = {}) {
const { delayFirstRequest, maxRetryCount, baseDelay, factor, maxDelay } =
this.options;
this.delayFirstRequest = delayFirstRequest ?? false;
this.maxRetryCount = maxRetryCount ?? 5;
this.baseDelay = baseDelay ?? 100;
this.factor = factor ?? 2;
this.maxDelay = maxDelay ?? -1;
}
public async request<T = unknown>(
request: Request<T>,
): Promise<HttpResponse<T>> {
let response: HttpResponse<T>;
let retryCount = 0;
let isSuccessfulHttpStatus = false;
let isTooManyRequests = false;
let isAtRetryLimit = false;
let delay = this.baseDelay;
do {
if (this.getShouldDelay(retryCount)) {
await Sleep(delay);
}
retryCount += 1;
response = await request.do();
isSuccessfulHttpStatus = getIsSuccessfulHttpStatus(response.status);
isTooManyRequests = response.status === this.TOO_MANY_REQUESTS_STATUS;
isAtRetryLimit = this.getIsAtRetryMax(retryCount);
delay *= this.factor * retryCount;
// set delay to max delay if delay is greater than max delay
if (this.maxDelay > -1 && delay > this.maxDelay) {
delay = this.maxDelay;
}
} while (!isSuccessfulHttpStatus && !isTooManyRequests && !isAtRetryLimit);
return response;
}
private getIsAtRetryMax(retryCount: number): boolean {
return this.maxRetryCount === 0 ? false : retryCount >= this.maxRetryCount;
}
private getShouldDelay(retryCount: number): boolean {
return retryCount !== 0 || this.delayFirstRequest;
}
}