RequestExecutor
Lower-level wrapper for a single REST request with retry, timeout (via AbortController), Retry-After header support, and backoff. This is the class createRestClient() uses internally for retry — use it directly when you want retry/backoff without the rest of RestClient (caching, rate limiting, auth, …).
import { RequestExecutor } from 'rest-pipeline-js'
const executor = new RequestExecutor({
baseURL: 'https://api.example.com',
retry: {
attempts: 3,
delayMs: 500,
backoffMultiplier: 2,
retriableStatus: [429, 500, 502, 503],
maxRetryAfterMs: 30000, // cap Retry-After at 30 s
},
})
// 5th arg: external AbortSignal (e.g. from orchestrator.abort())
const res = await executor.execute('/data', undefined, 3, 5000, signal)When the server returns a Retry-After header (numeric seconds or HTTP-date), that delay takes priority over the backoff formula. Values exceeding maxRetryAfterMs are clamped to the cap. Timeout is enforced via AbortController — the actual HTTP request is cancelled, not just the promise.
Constructor
new RequestExecutor(httpConfig: HttpConfig)Takes the same HttpConfig as createRestClient() — in practice, RequestExecutor itself only reads httpConfig.retry and uses the rest to construct its underlying HTTP client.
Methods
execute(command, reqConfig?, retryCount?, timeoutMs?, externalSignal?)
execute<T = unknown>(
command: string,
reqConfig?: RestRequestConfig,
retryCount?: number,
timeoutMs?: number,
externalSignal?: AbortSignal,
): Promise<ApiResponse<T>>Executes a single request with retry/backoff/timeout.
| Parameter | Type | Default | Description |
|---|---|---|---|
command | string | — | Request URL (relative to baseURL, if set) |
reqConfig | RestRequestConfig | — | Same request config accepted by RestClient's methods |
retryCount | number | httpConfig.retry.attempts ?? 0 | Override the number of retry attempts for this call |
timeoutMs | number | 10000 | Per-attempt timeout in ms, enforced via AbortController |
externalSignal | AbortSignal | — | An outside abort signal to cancel the request (e.g. orchestrator.abort()'s signal) |
Jitter strategies
retry.jitterStrategy controls how randomness is added to the computed backoff delay (it doesn't affect Retry-After, which is always used as-is):
"fixed"(default) —delayMs * backoffMultiplier^(attempt-1)plus up to +10% random jitter on top. Backward compatible; the delay never falls below the pure backoff value."full"—delay = random(0, delayMs * backoffMultiplier^(attempt-1)). Better at spreading out many concurrent retriers (avoids synchronized retry storms hitting the backend at the same instant), at the cost of individual delays sometimes being much shorter than the nominal backoff."decorrelated"—delay = min(cap, random(delayMs, prevDelay * 3)), whereprevDelaystarts atdelayMsand updates after each attempt, andcapisdelayMs * backoffMultiplier^attempts. Spreads out concurrent retriers even better than"full"since each client's next delay depends on its own previous one.
Both algorithms are from AWS's Exponential Backoff and Jitter post — use them when many instances of your app may retry against the same backend at once.
const executor = new RequestExecutor({
baseURL: 'https://api.example.com',
retry: { attempts: 5, delayMs: 200, backoffMultiplier: 2, jitterStrategy: 'full' },
})