Wrap fetch with retries, timeout, logging, caching, error handling and more
🚧 Work in progress, probably shouldn't be used yet unless you're me or you want to help debug/design 🚧
Aims:
- Transparently wrap fetch. Return functions that can be swapped in for
fetch, anywhere. - Work with all fetch implementations (Browsers, undici, node-fetch, minipass-fetch, make-fetch-happen, deno, bun)
- Be well-behaved, follow best practices.
- Be very small.
- Have no dependencies at all - users must even pass their own
fetchin. - Be very configurable.
- Work anywhere.
- Be very flexible. Work with popular tools:
zodfor parsingpinofor loggingdebugfor debuggingnextfor... stuffkeyvfor caching
- Be un-surprising and honest.
const {fetch: myfetch} = fetchomatic(fetch).withRetry({
shouldRetry: retry.createShouldRetry(
retry.retryOnFailure(),
retry.delayRetry({ms: 10}),
retry.expBackoff({power: 2}),
retry.capRetryAttempts({attempts: 4}),
retry.logRetry({logger: {...console, warn, error}}),
opts => {
const previous = opts.basis(opts)
if (typeof previous.retryAfterMs !== 'number') {
return previous
}
return {
...previous,
request: parsed => {
const headers = {...parsed.headers, retry_number: `${Number(parsed.headers.retry_number || 0) + 1}`}
return {headers}
},
}
},
),
})
await myfetch('https://example.com', {headers: {'user-agent': 'abc'}}) // myfetch can be used exactly like the built-in `fetch`Notes on how this implemented.
It's written in TypeScript, and it's currently using import statements like import {withRetry} from './retry.js'. Then TypeScript compiles it as CommonJS, and then a post-tsc script renames all files from dist/cjs/abc.js to dist/cjs/abc.cjs. Then there's a generate wrapper file, with all the same exports, so that ES Modules users can import the library without using createRequire. There might be OSS libraries that can do some of this automatically.