@resili/fetch
v0.1.0-alpha.1
Published
Native fetch adapter for Resili.
Downloads
22
Readme
@resili/fetch
Native
fetchadapter for Resili.
@resili/fetch wraps a fetch-compatible function with @resili/core, returning
a function with the same call shape as native fetch(input, init?).
Use this package when you want retry, timeout, circuit breaker, bulkhead, rate limiting, fallback, or custom policies around fetch calls without changing the rest of your code to a different HTTP client.
For the full framework overview, see the repository README.
Installation
pnpm add @resili/core @resili/fetchnpm install @resili/core @resili/fetchyarn add @resili/core @resili/fetchNode.js 20 or newer is required. By default, the adapter uses globalThis.fetch.
You may also inject a fetch-compatible implementation for tests.
Quick Start
import { createFetch } from "@resili/fetch";
const resilientFetch = createFetch({
timeout: { perAttemptMs: 1_000 },
retry: { maxAttempts: 3, jitter: "none" },
});
const response = await resilientFetch("https://api.example.com/users", {
method: "GET",
});The returned function is typed as:
(input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;createFetch()
import { createFetch, type FetchImplementation } from "@resili/fetch";
const fetchImplementation: FetchImplementation = async (input, init) => {
return await fetch(input, init);
};
const resilientFetch = createFetch({
fetch: fetchImplementation,
timeout: { perAttemptMs: 2_000 },
});createFetch() accepts the implemented @resili/core configuration fields:
| Field | Purpose |
| ---------------- | ------------------------------------------- |
| retry | Retry failed calls. |
| timeout | Apply per-attempt timeout behavior. |
| circuitBreaker | Stop calls while a dependency is unhealthy. |
| bulkhead | Bound concurrency and queue depth. |
| rateLimiter | Limit request rate in memory. |
| fallback | Return an alternate Response on errors. |
| classifier | Override failure classification. |
| store | Override the state store service. |
| clock | Override timers and time source. |
| policies | Register custom policy factories. |
The fetch option is adapter-specific and is removed before configuration is
passed to @resili/core.
Retry Example
import { createFetch } from "@resili/fetch";
const resilientFetch = createFetch({
retry: {
maxAttempts: 3,
backoff: "exponential",
baseDelayMs: 100,
maxDelayMs: 1_000,
jitter: "none",
},
});
const response = await resilientFetch("https://api.example.com/orders");Retry behavior is provided by @resili/core. Deterministic jitter: "none" is
supported by the current retry policy.
Timeout Example
import { createFetch } from "@resili/fetch";
const resilientFetch = createFetch({
timeout: { perAttemptMs: 750 },
});
const response = await resilientFetch("https://api.example.com/health");The adapter shallow-copies RequestInit and sets init.signal to the Resili
context signal for each execution. Resili's signal overrides a caller-provided
init.signal.
Fallback Example
import { createFetch } from "@resili/fetch";
const resilientFetch = createFetch({
timeout: { perAttemptMs: 500 },
fallback: {
handler() {
return new Response("cached fallback", { status: 200 });
},
},
});
const response = await resilientFetch("https://api.example.com/status");Fallback handlers may return a Response or a promise for one.
Custom Policies
import { definePolicy } from "@resili/core";
import { createFetch } from "@resili/fetch";
const headerPolicy = definePolicy({
name: "headers-observer",
order: { before: "timeout" },
create() {
return {
name: "headers-observer",
order: { before: "timeout" },
execute(ctx, next) {
console.log(ctx.requestId);
return next(ctx);
},
};
},
});
const resilientFetch = createFetch({
policies: [{ factory: headerPolicy }],
});Testing With an Injected Fetch
import { createFetch, type FetchImplementation } from "@resili/fetch";
const fakeFetch: FetchImplementation = async () => {
return new Response("ok", { status: 200 });
};
const resilientFetch = createFetch({ fetch: fakeFetch });
const response = await resilientFetch("https://example.test");Injected fetch implementations are useful for deterministic tests and for custom runtime environments.
Current Limitations
- The adapter does not classify HTTP status codes.
- The adapter does not transform response bodies.
- The adapter does not retry based on
Response.statusby itself. - The adapter does not add headers or mutate caller-provided
RequestInit. - The adapter does not provide OpenTelemetry or metrics exporters.
- The adapter is intentionally a thin wrapper over
@resili/core.
If you need status-code classification, provide a core classifier or custom policy that matches your application contract.
Documentation
License
MIT © Resili contributors.
