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
fetch
in. - Be very configurable.
- Work anywhere.
- Be very flexible. Work with popular tools:
zod
for parsingpino
for loggingdebug
for debuggingnext
for... stuffkeyv
for 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.