Why fetch-smartly?
The Problem
The native fetch API is powerful but bare-bones. In production, HTTP requests fail — servers go down, networks drop, rate limits hit, timeouts expire. Handling all of these correctly means writing the same retry logic, timeout management, and error classification code in every project.
Most developers either:
- Ignore failures — leading to silent data loss and broken UIs
- Use heavyweight libraries — pulling in dozens of dependencies for features they don't need
- Roll their own — reinventing exponential backoff, circuit breakers, and abort signal management every time
The Solution
fetch-smartly wraps the native fetch API with production-grade resilience patterns, zero dependencies, and full TypeScript support:
- Intelligent Retry — Exponential backoff with jitter. Respects
Retry-Afterheaders. Never retries client errors (400, 401, 404). - Typed Errors — Every failure is a specific error class (
NetworkError,TimeoutError,HttpError,RateLimitError) with structured metadata andinstanceofsupport. - Circuit Breaker — Automatic failure isolation prevents cascading failures. Open → half-open → closed state machine.
- Request Deduplication — Concurrent identical GET requests share one fetch call. No wasted bandwidth.
- Offline Queue — Queue failed mutations for replay when connectivity returns. Pluggable storage backends.
- Isomorphic — Works in Node.js 18+, browsers, Cloudflare Workers, Deno, and Bun.
- Tiny — Under 16KB bundled. No bloat.
Design Principles
- Zero dependencies — Only native Web APIs. No supply chain risk.
- Strict TypeScript — No
any. Full type safety and autocompletion. - Composable — Use only the pieces you need.
executeRequestfor single calls,fetchWithRetryfor retry,CircuitBreakerandDedupManageras standalone utilities. - No magic — Explicit configuration over hidden defaults. You control every behavior.