@http-client-toolkit/core
v3.0.0
Published
Core HTTP client with pluggable caching, deduplication, and rate limiting
Maintainers
Readme
@http-client-toolkit/core
Core HTTP client with pluggable caching, deduplication, and rate limiting. Part of the http-client-toolkit monorepo.
Installation
npm install @http-client-toolkit/coreRequires Node.js >= 20.
You'll also need at least one store backend:
npm install @http-client-toolkit/store-memory
# or
npm install @http-client-toolkit/store-sqliteQuick Start
import { HttpClient } from '@http-client-toolkit/core';
import {
InMemoryCacheStore,
InMemoryDedupeStore,
InMemoryRateLimitStore,
} from '@http-client-toolkit/store-memory';
const client = new HttpClient({
name: 'example-api',
cache: new InMemoryCacheStore(),
dedupe: new InMemoryDedupeStore(),
rateLimit: new InMemoryRateLimitStore(),
cacheTTL: 300,
});
const data = await client.get<{ name: string }>(
'https://api.example.com/user/1',
);Every store is optional. Use only what you need:
// Cache-only client
const client = new HttpClient({
name: 'cached',
cache: new InMemoryCacheStore(),
});
// Rate-limited client with no caching
const client = new HttpClient({
name: 'rate-limited',
rateLimit: new InMemoryRateLimitStore({
defaultConfig: { limit: 100, windowMs: 60_000 },
}),
});Recommended Usage
Create a thin wrapper module per third-party API so callers don't configure anything and per-request tuning lives in one place. See the Recommended Usage guide for a full walkthrough.
API
new HttpClient(options)
HttpClient exposes a single request method: get(url, options?). The url must be an absolute URL.
Request options (client.get)
| Property | Type | Default | Description |
| ---------------- | ------------------------ | -------------- | ------------------------------------------------------------------- |
| signal | AbortSignal | - | Cancels wait + request when aborted |
| priority | 'user' \| 'background' | 'background' | Used by adaptive rate-limit stores |
| headers | Record<string, string> | - | Custom request headers (also used for Vary-based cache matching) |
| retry | RetryOptions \| false | - | Per-request retry config; false disables retries for this call |
| cacheTTL | number | - | Per-request cache TTL in seconds (overrides constructor) |
| cacheOverrides | CacheOverrideOptions | - | Per-request cache overrides (shallow-merged with constructor-level) |
Constructor options:
| Property | Type | Default | Description |
| --------------------- | ------------------------------------------ | -------- | --------------------------------------- |
| name | string | required | Name for the client instance |
| cache | CacheStore | - | Response caching |
| dedupe | DedupeStore | - | Request deduplication |
| rateLimit | RateLimitStore \| AdaptiveRateLimitStore | - | Rate limiting |
| cacheTTL | number | 3600 | Cache TTL when response has no headers |
| throwOnRateLimit | boolean | true | Throw when rate limited vs. wait |
| maxWaitTime | number | 60000 | Max wait time (ms) before throwing |
| responseTransformer | (data: unknown) => unknown | - | Transform raw response data |
| responseHandler | (data: unknown) => unknown | - | Validate/process transformed data |
| errorHandler | (error: unknown) => Error | - | Convert errors to domain-specific types |
| cacheOverrides | CacheOverrideOptions | - | Override cache header behaviors |
| retry | RetryOptions \| false | - | Retry config; false disables globally |
| rateLimitHeaders | RateLimitHeaderConfig | defaults | Configure standard/custom header names |
Request Flow
- Cache - Return cached response if available
- Dedupe - If an identical request is already in-flight, wait for its result
- Rate Limit - Wait or throw if the rate limit is exceeded
- Fetch - Execute the HTTP request
- Transform & Validate - Apply
responseTransformerthenresponseHandler - Store - Cache the result, record the rate limit hit, and resolve any deduplicated waiters
Error Handling
All HTTP errors are wrapped in HttpClientError:
import { HttpClientError } from '@http-client-toolkit/core';
try {
await client.get(url);
} catch (error) {
if (error instanceof HttpClientError) {
console.log(error.message);
console.log(error.statusCode);
}
}Cancellation
Pass an AbortSignal to cancel a request, including while waiting for a rate limit window:
const controller = new AbortController();
const data = await client.get(url, { signal: controller.signal });
controller.abort();Header-Based Rate Limiting
HttpClient respects server-provided rate-limit headers out of the box:
Retry-AfterRateLimit-Remaining/RateLimit-ResetX-RateLimit-Remaining/X-RateLimit-Reset
Map non-standard header names per API:
const client = new HttpClient({
name: 'custom-api',
rateLimitHeaders: {
retryAfter: ['RetryAfterSeconds'],
remaining: ['Remaining-Requests'],
reset: ['Window-Reset-Seconds'],
},
});Exports
HttpClient- Main client classHttpClientError- Error class withstatusCodehashRequest- Deterministic SHA-256 request hashing- Store interfaces:
CacheStore,DedupeStore,RateLimitStore,AdaptiveRateLimitStore
License
ISC
