This repository has been archived by the owner on Feb 12, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 93df2f8
Showing
6 changed files
with
2,667 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
let fetch; | ||
|
||
try { | ||
fetch = require('node-fetch'); | ||
} catch (err) { | ||
if (err.code === 'MODULE_NOT_FOUND' && /node-fetch/.test(err.message)) { | ||
console.error('Could not find peer dependency `node-fetch`. ' + | ||
'Make sure it is installed: `yarn add node-fetch`'); | ||
} | ||
throw err; | ||
} | ||
|
||
const retry = require('async-retry'); | ||
const debug = require('debug')('fetch-retry'); | ||
|
||
// retry settings | ||
const MIN_TIMEOUT = 10; | ||
const MAX_RETRIES = 3; | ||
const FACTOR = 5; | ||
|
||
const fetchRetry = (url, opts = {}, retryOpts) => ( | ||
retry(async (bail, attempt) => { | ||
const {method = 'GET'} = opts; | ||
try { | ||
// this will be retried | ||
const res = await fetch(url, opts); | ||
debug('status %d', res.status); | ||
if (res.status >= 500 && res.status < 600) { | ||
throw err; | ||
} else { | ||
return res; | ||
} | ||
} catch (err) { | ||
debug(`${method} ${url} error (${res.status}). ${attempt < MAX_RETRIES ? 'retrying' : ''}`, err); | ||
throw err; | ||
} | ||
}, retryOpts || { | ||
// timeouts will be [ 10, 50, 250 ] | ||
minTimeout: MIN_TIMEOUT, | ||
retries: MAX_RETRIES, | ||
factor: FACTOR | ||
}) | ||
); | ||
|
||
module.exports = fetchRetry; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"name": "@zeit/fetch-retry", | ||
"devDependencies": { | ||
"jest": "^21.2.1", | ||
"node-fetch": "^1.7.3" | ||
}, | ||
"dependencies": { | ||
"async-retry": "^1.1.3" | ||
}, | ||
"scripts": { | ||
"test": "jest test" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
# fetch-retry | ||
|
||
A layer on top of `fetch` (via [node-fetch](https://www.npmjs.com/package/node-fetch) | ||
with sensible defaults for retrying to prevent common errors. | ||
|
||
## How to use | ||
|
||
`fetch-retry` is a drop-in replacement for `fetch`: | ||
|
||
```js | ||
const fetch = require('@zeit/fetch-retry'); | ||
module.exports = async () => { | ||
const res = await fetch('http://localhost:3000') | ||
console.log(res.status); | ||
} | ||
``` | ||
|
||
Make sure to `yarn add fetch-retry` in your main package. | ||
|
||
The third optional parameter is custom [retry options](https://github.com/zeit/async-retry) | ||
passed to `async-retry`. | ||
|
||
## Rationale | ||
|
||
Some errors are very common in production (like the underlying `Socket` | ||
yielding `ECONNRESET`), and can easily and instantly be remediated | ||
by retrying. | ||
|
||
The default behavior of `fetch-retry` is to attempt retries **10**, **50** | ||
and **250** milliseconds (a total of 3 retires) after | ||
a *network error* or *5xx* error occur. | ||
|
||
The idea is to provide a sensible default: most applications should | ||
continue to perform correctly with a worst case scenario of a given | ||
request having an additional 250ms overhead. | ||
|
||
On the other hand, most applications that use `fetch-retry` instead of | ||
vanilla `fetch` should see lower rates of common errors and fewer 'glitches' | ||
in production. | ||
|
||
## Tests | ||
|
||
To run rests, execute | ||
|
||
```console | ||
npm test | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
const {createServer} = require('http'); | ||
const retryFetch = require('./index'); | ||
|
||
test('retries upon 500', async () => { | ||
let i = 0 | ||
const server = createServer((req, res) => { | ||
if (i++ < 2) { | ||
res.writeHead(500); | ||
res.end(); | ||
} else { | ||
res.end('ha'); | ||
} | ||
}); | ||
|
||
return new Promise((resolve, reject) => { | ||
server.listen(async () => { | ||
const {port} = server.address(); | ||
try { | ||
const res = await retryFetch(`http://127.0.0.1:${port}`); | ||
expect(await res.text()).toBe('ha'); | ||
server.close(); | ||
resolve(); | ||
} catch (err) { | ||
reject(err); | ||
} | ||
}); | ||
server.on('error', reject); | ||
}); | ||
}); |
Oops, something went wrong.