


@gkoos/ffetch
A production-ready TypeScript-first drop-in replacement for native fetch, or any fetch-compatible implementation.
ffetch can wrap any fetch-compatible implementation (native fetch, node-fetch, undici, or framework-provided fetch), making it flexible for SSR, edge, and custom environments.
Key Features:
- Timeouts – per-request or global
- Retries – exponential backoff + jitter
- Circuit breaker – automatic failure protection
- Hooks – logging, auth, metrics, request/response transformation
- Pending requests – real-time monitoring of active requests
- Per-request overrides – customize behavior on a per-request basis
- Universal – Node.js, Browser, Cloudflare Workers, React Native
- Zero runtime deps – ships as dual ESM/CJS
Quick Start
Install
npm install @gkoos/ffetch
Basic Usage
import createClient from '@gkoos/ffetch'
const api = createClient({
timeout: 5000,
retries: 3,
retryDelay: ({ attempt }) => 2 ** attempt * 100 + Math.random() * 100,
})
const response = await api('https://api.example.com/users')
const data = await response.json()
Using a Custom fetchHandler (SSR, metaframeworks, or polyfills)
import createClient from '@gkoos/ffetch'
const api = createClient({
fetchHandler: fetch,
timeout: 5000,
})
import nodeFetch from 'node-fetch'
const apiNode = createClient({ fetchHandler: nodeFetch })
const response = await api('/api/data')
Advanced Example
const client = createClient({
timeout: 10000,
retries: 2,
circuit: { threshold: 5, reset: 30000 },
fetchHandler: fetch,
hooks: {
before: async (req) => console.log('→', req.url),
after: async (req, res) => console.log('←', res.status),
onError: async (req, err) => console.error('Error:', err.message),
onCircuitOpen: (req) => console.warn('Circuit opened due to:', req.url),
onCircuitClose: (req) => console.info('Circuit closed after:', req.url),
},
})
try {
const response = await client('/api/data')
if (!response.ok) {
console.log('HTTP error:', response.status)
return
}
const data = await response.json()
console.log('Active requests:', client.pendingRequests.length)
} catch (err) {
if (err instanceof TimeoutError) {
console.log('Request timed out')
} else if (err instanceof RetryLimitError) {
console.log('Request failed after retries')
}
}
Documentation
Complete Documentation | Start here - Documentation index and overview |
API Reference | Complete API documentation and configuration options |
Advanced Features | Per-request overrides, pending requests, circuit breakers, custom errors |
Hooks & Transformation | Lifecycle hooks, authentication, logging, request/response transformation |
Usage Examples | Real-world patterns: REST clients, GraphQL, file uploads, microservices |
Compatibility | Browser/Node.js support, polyfills, framework integration |
Environment Requirements
ffetch
requires modern AbortSignal APIs:
- Node.js 20.6+ (for AbortSignal.any)
- Modern browsers (Chrome 117+, Firefox 117+, Safari 17+, Edge 117+)
If your environment does not support AbortSignal.any
(Node.js < 20.6, older browsers), you must install a polyfill before using ffetch. See the compatibility guide for instructions.
Custom fetch support:
You can pass any fetch-compatible implementation (native fetch, node-fetch, undici, SvelteKit, Next.js, Nuxt, or a polyfill) via the fetchHandler
option. This makes ffetch fully compatible with SSR, edge, metaframework environments, custom backends, and test runners.
"AbortSignal.any is not a function"
Solution: Install a polyfill for AbortSignal.any
npm install abort-controller-x
CDN Usage
<script type="module">
import createClient from 'https://unpkg.com/@gkoos/ffetch/dist/index.min.js'
const api = createClient({ timeout: 5000 })
const data = await api('/api/data').then((r) => r.json())
</script>
Fetch vs. Axios vs. ffetch
Timeouts | ❌ Manual AbortController | ✅ Built-in | ✅ Built-in with fallbacks |
Retries | ❌ Manual implementation | ❌ Manual or plugins | ✅ Smart exponential backoff |
Circuit Breaker | ❌ Not available | ❌ Manual or plugins | ✅ Automatic failure protection |
Request Monitoring | ❌ Manual tracking | ❌ Manual tracking | ✅ Built-in pending requests |
Error Types | ❌ Generic errors | ⚠️ HTTP errors only | ✅ Specific error classes |
TypeScript | ⚠️ Basic types | ⚠️ Basic types | ✅ Full type safety |
Hooks/Middleware | ❌ Not available | ✅ Interceptors | ✅ Comprehensive lifecycle hooks |
Bundle Size | ✅ Native (0kb) | ❌ ~13kb minified | ✅ ~3kb minified |
Modern APIs | ✅ Web standards | ❌ XMLHttpRequest | ✅ Fetch + modern features |
Custom Fetch Support | ❌ No (global only) | ❌ No | ✅ Yes (wrap any fetch-compatible implementation, including framework or custom fetch) |
Contributing
License
MIT © 2025 gkoos