Package Exports
- @http-client-toolkit/core
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 |
resourceKeyResolver |
(url: string) => string |
URL origin | Customize how rate-limit resource keys are derived |
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'],
},
});Custom Rate-Limit Buckets
Use resourceKeyResolver when list and retrieve routes should share the same
rate-limit bucket:
const client = new HttpClient({
name: 'issues-api',
resourceKeyResolver: (url) => {
const path = new URL(url).pathname;
if (path === '/api/issues' || path.startsWith('/api/issue/')) {
return 'issues';
}
return new URL(url).origin;
},
rateLimit: {
store: rateLimitStore,
},
});rateLimit.resourceExtractor is deprecated and kept temporarily for backward
compatibility.
Exports
HttpClient- Main client classHttpClientError- Error class withstatusCodehashRequest- Deterministic SHA-256 request hashing- Store interfaces:
CacheStore,DedupeStore,RateLimitStore,AdaptiveRateLimitStore
License
ISC