enterprise-fetch 1.0.0-alpha.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +144 -87
- package/dist/client/enterprise-fetch.cjs.js +908 -177
- package/dist/client/enterprise-fetch.cjs.min.js +1 -1
- package/dist/client/enterprise-fetch.esm.js +16 -23
- package/dist/client/enterprise-fetch.esm.min.js +1 -1
- package/dist/enterprise-fetch.cjs.js +154 -0
- package/dist/enterprise-fetch.d.ts +2 -1
- package/dist/enterprise-fetch.esm.js +123 -0
- package/package.json +94 -78
- package/dist/enterprise-fetch.js +0 -54
- package/dist/enterprise-fetch.js.map +0 -1
- package/dist/fetch-defaults.js +0 -45
- package/dist/fetch-defaults.js.map +0 -1
- package/dist/models/AppError.js +0 -2
- package/dist/models/AppError.js.map +0 -1
- package/dist/models/EnterpriseFetch.js +0 -2
- package/dist/models/EnterpriseFetch.js.map +0 -1
- package/dist/models/index.js +0 -3
- package/dist/models/index.js.map +0 -1
- package/dist/util/client/fetch.d.ts +0 -1
- package/dist/util/client/fetch.js +0 -2
- package/dist/util/client/fetch.js.map +0 -1
- package/dist/util/client/is-client.js +0 -3
- package/dist/util/client/is-client.js.map +0 -1
- package/dist/util/parsejson.js +0 -18
- package/dist/util/parsejson.js.map +0 -1
- package/dist/util/server/fetch.js +0 -3
- package/dist/util/server/fetch.js.map +0 -1
- package/dist/util/server/proxy-agent.js +0 -14
- package/dist/util/server/proxy-agent.js.map +0 -1
- package/dist/util/typeguards.js +0 -3
- package/dist/util/typeguards.js.map +0 -1
- package/package-lock.json +0 -6098
package/README.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
# Enterprise Fetch
|
|
1
|
+
# Enterprise Fetch [](https://codecov.io/gh/neilflatley/enterprise-fetch) [](https://github.com/neilflatley/enterprise-fetch/actions/workflows/node.js.yml) 
|
|
2
2
|
|
|
3
3
|
A fetch wrapper with some nice enterprise features that are missing from the default fetch implementations, such as request timeout, request proxy, and a fully configurable retry policy. Designed to work in both browser and NodeJS server environments.
|
|
4
4
|
|
|
5
|
+
ES6 module and CJS available for developers and pre-built minified bundles that can be used straight in the browser
|
|
6
|
+
|
|
5
7
|
## Simple usage
|
|
6
8
|
|
|
7
9
|
The default export provides a fetch wrapper with a default retry policy set
|
|
@@ -10,39 +12,42 @@ Use it as a drop-in replacement for your existing fetch implementation
|
|
|
10
12
|
|
|
11
13
|
### Examples
|
|
12
14
|
|
|
13
|
-
```
|
|
15
|
+
```javascript
|
|
14
16
|
import enterpriseFetch from 'enterprise-fetch';
|
|
15
17
|
|
|
16
18
|
// enterpriseFetch(url: RequestInfo, init?: FetchInit): Promise<Response>
|
|
17
19
|
|
|
18
20
|
enterpriseFetch('https://httpbin.org/json')
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
.then((res) => res.json())
|
|
22
|
+
.then((json) => console.log(json));
|
|
21
23
|
|
|
22
24
|
enterpriseFetch('https://httpbin.org/post', {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
method: 'post',
|
|
26
|
+
body: JSON.stringify({ a: 1 }),
|
|
27
|
+
headers: { 'Content-Type': 'application/json' },
|
|
28
|
+
})
|
|
29
|
+
.then((res) => res.json())
|
|
30
|
+
.then((json) => console.log(json));
|
|
29
31
|
```
|
|
30
32
|
|
|
31
33
|
#### Async / await
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
I prefer writing JavaScript in this way and would be the recommended approach where possible
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
34
38
|
const res = await enterpriseFetch('https://httpbin.org/post', {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
method: 'post',
|
|
40
|
+
body: JSON.stringify({ a: 1 }),
|
|
41
|
+
headers: { 'Content-Type': 'application/json' },
|
|
42
|
+
});
|
|
39
43
|
const json = await res.json();
|
|
40
44
|
console.log(json);
|
|
41
45
|
```
|
|
42
46
|
|
|
43
47
|
### Default retry policy
|
|
44
48
|
|
|
45
|
-
```
|
|
49
|
+
```javascript
|
|
50
|
+
const fetchDefaults = {
|
|
46
51
|
// The timeout to apply before sending Abort signal
|
|
47
52
|
// to all requests that do not supply a timeout option
|
|
48
53
|
timeout: 60 * 1000,
|
|
@@ -52,45 +57,36 @@ console.log(json);
|
|
|
52
57
|
minTimeout: 400,
|
|
53
58
|
factor: 2,
|
|
54
59
|
},
|
|
55
|
-
// Do retry function to examine failures and apply custom
|
|
60
|
+
// Do retry function to examine failures and apply custom
|
|
56
61
|
// retry logic return true to retry the fetch call
|
|
57
62
|
doRetry: async (attempt, res, { url, options }) => {
|
|
58
63
|
// Get the retry policy from options or fetchDefaults
|
|
59
64
|
const { retry = fetchDefaults.retry } = options || fetchDefaults;
|
|
60
65
|
if (attempt <= retry.retries) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if ('message' in res)
|
|
64
|
-
console.warn(
|
|
65
|
-
`[fetch] Attempt ${counter} ${
|
|
66
|
-
res.name || ('type' in res && res.type)
|
|
67
|
-
}: ${res.message} ${url || ''}`
|
|
68
|
-
);
|
|
69
|
-
else
|
|
70
|
-
console.warn(
|
|
71
|
-
`[fetch] Attempt ${counter} ${res.status}: ${res.statusText} ${
|
|
72
|
-
url || ''
|
|
73
|
-
}`
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
// Retry request on any network error,
|
|
66
|
+
// Retry request on any network error,
|
|
77
67
|
// or 4xx or 5xx status codes. No retry on 404
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
68
|
+
if (!res.status || (res.status >= 400 && res.status !== 404)) {
|
|
69
|
+
const counter = `${attempt}/${retry.retries}`;
|
|
70
|
+
if ('message' in res)
|
|
71
|
+
console.warn(
|
|
72
|
+
`[fetch] Attempt ${counter} ${
|
|
73
|
+
res.name || ('type' in res && res.type)
|
|
74
|
+
}: ${res.message} ${url || ''}`
|
|
75
|
+
);
|
|
76
|
+
else
|
|
77
|
+
console.warn(
|
|
78
|
+
`[fetch] Attempt ${counter} ${res.status}: ${res.statusText} ${
|
|
79
|
+
url || ''
|
|
80
|
+
}`
|
|
81
|
+
);
|
|
86
82
|
return true;
|
|
87
83
|
}
|
|
88
84
|
}
|
|
89
85
|
return false;
|
|
90
86
|
},
|
|
87
|
+
};
|
|
91
88
|
```
|
|
92
89
|
|
|
93
|
-
|
|
94
90
|
## Advanced usage
|
|
95
91
|
|
|
96
92
|
We can garnish any request `init` object with some additional options as well as the full set of `RequestInit` options supported by the standard fetch implementation
|
|
@@ -99,10 +95,10 @@ We can garnish any request `init` object with some additional options as well as
|
|
|
99
95
|
|
|
100
96
|
Uses `AbortController` signal instance to terminate the request after a specified timeout duration (in ms).
|
|
101
97
|
|
|
102
|
-
```
|
|
98
|
+
```javascript
|
|
103
99
|
enterpriseFetch('https://httpbin.org/json', {
|
|
104
|
-
|
|
105
|
-
|
|
100
|
+
timeout: 5000,
|
|
101
|
+
})
|
|
106
102
|
.then((res) => res.json())
|
|
107
103
|
.then((json) => console.log(json));
|
|
108
104
|
```
|
|
@@ -115,15 +111,15 @@ e.g. wait 1000ms after first failed request, wait 1500ms between the second and
|
|
|
115
111
|
|
|
116
112
|
This is simply the options object from https://www.npmjs.com/package/promise-retry
|
|
117
113
|
|
|
118
|
-
```
|
|
114
|
+
```javascript
|
|
119
115
|
enterpriseFetch('https://httpbin.org/json', {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
116
|
+
retry: {
|
|
117
|
+
retries: 3,
|
|
118
|
+
minTimeout: 1000,
|
|
119
|
+
factor: 500,
|
|
120
|
+
},
|
|
121
|
+
timeout: 5000,
|
|
122
|
+
})
|
|
127
123
|
.then((res) => res.json())
|
|
128
124
|
.then((json) => console.log(json));
|
|
129
125
|
```
|
|
@@ -134,49 +130,52 @@ Custom retry logic is required to exploit domain specific rules when deciding if
|
|
|
134
130
|
|
|
135
131
|
For example, checking the headers for existance of a specific error key, or re-setting use-once headers such as an OAuth 1.0 bearer token for the next request
|
|
136
132
|
|
|
137
|
-
```
|
|
133
|
+
```javascript
|
|
138
134
|
enterpriseFetch('https://httpbin.org/json', {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
135
|
+
retry: {
|
|
136
|
+
retries: 3,
|
|
137
|
+
minTimeout: 1000,
|
|
138
|
+
factor: 500,
|
|
139
|
+
},
|
|
140
|
+
timeout: 5000,
|
|
141
|
+
doRetry: async (attempt, response, { options }) => {
|
|
142
|
+
const { retry } = options;
|
|
143
|
+
if (attempt <= retry.retries) {
|
|
144
|
+
// Retry request on any network error,
|
|
145
|
+
// or any 4xx or 5xx status codes, except 404
|
|
146
|
+
if (!res.status || (res.status >= 400 && res.status !== 404)) {
|
|
148
147
|
const counter = `${attempt}/${retry.retries}`;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
148
|
+
if ('message' in res)
|
|
149
|
+
console.warn(
|
|
150
|
+
`[fetch] Attempt ${counter} ${
|
|
151
|
+
res.name || ('type' in res && res.type)
|
|
152
|
+
}: ${res.message} ${url || ''}`
|
|
153
|
+
);
|
|
154
|
+
else
|
|
155
|
+
console.warn(
|
|
156
|
+
`[fetch] Attempt ${counter} ${res.status}: ${res.statusText} ${
|
|
157
|
+
url || ''
|
|
158
|
+
}`
|
|
159
|
+
);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return false;
|
|
164
|
+
},
|
|
165
|
+
})
|
|
166
|
+
.then((res) => res.json())
|
|
167
|
+
.then((json) => console.log(json));
|
|
169
168
|
```
|
|
170
169
|
|
|
171
170
|
## Application level retry policy
|
|
172
171
|
|
|
173
|
-
Create a fetch retry policy that can be used throughout your application.
|
|
172
|
+
Create a fetch retry policy that can be used throughout your application.
|
|
174
173
|
|
|
175
174
|
Create a configuration file in your app to define fetch defaults that can be applied to all requests using this fetch wrapper.
|
|
176
175
|
|
|
177
176
|
You can then create an instance of `fetchWithDefaults(fetchDefaults)`, supplying your `fetchDefaults`. Then with this you can either redefine global fetch, or export the created instance of `fetchWithDefaults(fetchDefaults)` to import wherever it is needed in your app.
|
|
178
177
|
|
|
179
|
-
```
|
|
178
|
+
```javascript
|
|
180
179
|
import { fetchWithDefaults } from 'enterprise-fetch';
|
|
181
180
|
import { FetchInit } from 'enterprise-fetch/dist/models';
|
|
182
181
|
|
|
@@ -234,8 +233,66 @@ const fetch = fetchWithDefaults(fetchDefaults);
|
|
|
234
233
|
export default fetch;
|
|
235
234
|
```
|
|
236
235
|
|
|
237
|
-
## Proxying requests
|
|
236
|
+
## Proxying requests (NodeJS)
|
|
238
237
|
|
|
239
238
|
NodeJS only, we can leverage the `https-proxy-agent` to send our requests to a proxy server via the `agent` option in `RequestInit`, this is very useful for debugging and tracing fetch requests which is not natively possible with NodeJS.
|
|
240
239
|
|
|
241
|
-
To activate set process env variable `http_proxy` to a valid http proxy server. e.g. `process.env.http_proxy = http://127.0.0.1:8888`
|
|
240
|
+
To activate set process env variable `http_proxy` to a valid http proxy server. e.g. `process.env.http_proxy = http://127.0.0.1:8888`
|
|
241
|
+
|
|
242
|
+
## Browser bundles
|
|
243
|
+
|
|
244
|
+
Browser bundles are available for you to use if you are not using a module bundler for your application.
|
|
245
|
+
|
|
246
|
+
### Simple example
|
|
247
|
+
|
|
248
|
+
```html
|
|
249
|
+
<html>
|
|
250
|
+
<head>
|
|
251
|
+
<script
|
|
252
|
+
type="text/javascript"
|
|
253
|
+
src="https://unpkg.com/enterprise-fetch"
|
|
254
|
+
></script>
|
|
255
|
+
<script>
|
|
256
|
+
var element = function (id) {
|
|
257
|
+
return document.getElementById(id);
|
|
258
|
+
};
|
|
259
|
+
var goFetch = function () {
|
|
260
|
+
var uri = element('uri').value;
|
|
261
|
+
window['enterprise-fetch'].default(uri).then(function (response) {
|
|
262
|
+
alert(response.status);
|
|
263
|
+
});
|
|
264
|
+
};
|
|
265
|
+
</script>
|
|
266
|
+
</head>
|
|
267
|
+
<body>
|
|
268
|
+
<input type="text" id="uri" value="https://httpbin.org/status/500" />
|
|
269
|
+
<input type="button" id="fetch" value="Fetch" onclick="goFetch()" />
|
|
270
|
+
</body>
|
|
271
|
+
</html>
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Legacy browsers
|
|
275
|
+
|
|
276
|
+
A `cjs` bundle exists in addition to the `esm` bundle loaded by default if you require legacy browser support. The `cjs` bundle is 2x the size of the `esm` bundle due to the syntax transpilation and polyfills.
|
|
277
|
+
|
|
278
|
+
Polyfills will be required for browsers that do not have native support for
|
|
279
|
+
|
|
280
|
+
- AbortController
|
|
281
|
+
- fetch
|
|
282
|
+
|
|
283
|
+
You could use a polyfill service to polyfill missing features by adding a snippet like the below before loading this script
|
|
284
|
+
```html
|
|
285
|
+
<script
|
|
286
|
+
type="text/javascript"
|
|
287
|
+
src="https://polyfill.io/v3/polyfill.min.js?features=AbortController%2Cfetch"
|
|
288
|
+
></script>
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Development scripts
|
|
292
|
+
|
|
293
|
+
- `npm start` builds the library and runs through all tests in command line
|
|
294
|
+
- `npm run build` build the library from source and create an instrumented copy of the build for coverage reporting
|
|
295
|
+
- `npm test` starts a http server and runs through all tests in command line
|
|
296
|
+
- `npm run cy:open` starts a http server and opens cypress testing gui for you to execute test specs interactively in a browser window
|
|
297
|
+
- `npm run report:coverage` checks code coverage and starts a http server rendering the test coverage report for review in a browser
|
|
298
|
+
- `npm run serve` starts a http server with a simple browser testing page available at the `/test` route
|