fch 3.0.0 → 3.0.1

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.
Files changed (3) hide show
  1. package/fetch.js +2 -2
  2. package/package.json +1 -1
  3. package/readme.md +144 -31
package/fetch.js CHANGED
@@ -145,8 +145,8 @@ const create = (defaults = {}) => {
145
145
  request = before(request);
146
146
  }
147
147
 
148
- // It should be cached
149
- if (dedupe) {
148
+ // It should be cached and it's not being manually manipulated
149
+ if (dedupe && !request.signal) {
150
150
  // It's already cached! Just return it
151
151
  if (dedupe.get()) return dedupe.get();
152
152
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fch",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "Fetch interface with better promises, deduplication, defaults, etc.",
5
5
  "homepage": "https://github.com/franciscop/fetch",
6
6
  "repository": "https://github.com/franciscop/fetch.git",
package/readme.md CHANGED
@@ -4,7 +4,7 @@ A tiny library to make API calls easier. Similar to Axios, but tiny size and sim
4
4
 
5
5
  ```js
6
6
  import api from "fch";
7
- const mew = await api("https://pokeapi.co/pokemon/150");
7
+ const mew = await api.get("https://pokeapi.co/pokemon/150");
8
8
  console.log(mew);
9
9
  ```
10
10
 
@@ -19,29 +19,30 @@ console.log(mew);
19
19
  - Configurable to return either just the body, or the full response.
20
20
 
21
21
  ```js
22
- // Calls and methods available:
23
- api(url, { method, body, headers, ...options })
22
+ import api from 'fch';
23
+
24
24
  api.get(url, { headers, ...options })
25
25
  api.head(url, { headers, ...options })
26
26
  api.post(url, { body, headers, ...options })
27
27
  api.patch(url, { body, headers, ...options })
28
28
  api.put(url, { body, headers, ...options })
29
29
  api.del(url, { body, headers, ...options })
30
- fch.create({ url, body, headers, ...options})
31
- ```
32
-
33
- |Options/variables |Default |Description |
34
- |------------------|---------------|-------------------------------------------|
35
- |`url` |`null` |The path or full url for the request |
36
- |`api.baseUrl` |`null` |The shared base of the API |
37
- |`api.method` |`"get"` |Default method to use for the call |
38
- |`api.query` |`{}` |Add query parameters to the URL |
39
- |`api.headers` |`{}` |Shared headers across all requests |
40
- |`api.dedupe` |`true` |Reuse GET requests made concurrently |
41
- |`api.output` |`"body"` |The return value of the API call |
42
- |`api.before` |`req => req` |Process the request before sending it |
43
- |`api.after` |`res => res` |Process the response before receiving it |
44
- |`api.error` |`err => reject(err)` |Process errors before returning them |
30
+
31
+ api.create({ url, body, headers, ...options})
32
+ ```
33
+
34
+ | Options | Default | Description |
35
+ |-----------|--------------------|---------------------------------------------|
36
+ | `url` | `null` | The path or full url for the request |
37
+ | `baseUrl` | `null` | The shared base of the API |
38
+ | `method` | `"get"` | Default method to use for the call |
39
+ | `query` | `{}` | Add query parameters to the URL |
40
+ | `headers` | `{}` | Shared headers across all requests |
41
+ | `dedupe` | `true` | Reuse GET requests made concurrently |
42
+ | `output` | `"body"` | The return value of the API call |
43
+ | `before` | `req => req` | Process the request before sending it |
44
+ | `after` | `res => res` | Process the response before returning it |
45
+ | `error` | `err => throw err` | Process errors before returning them |
45
46
 
46
47
  ## Getting Started
47
48
 
@@ -73,9 +74,10 @@ On the browser you can add it with a script and it will be available as `fch`:
73
74
  ```js
74
75
  import api from 'fch';
75
76
 
76
- // General options with their defaults; most of these are also parameters:
77
- api.baseUrl = null; // Set an API endpoint
77
+ // General options with their defaults; all of these are also parameters:
78
+ api.baseUrl = null; // Set an API base URL reused all across requests
78
79
  api.method = 'get'; // Default method to use for api()
80
+ api.query = {}; // Is merged with the query parameters passed manually
79
81
  api.headers = {}; // Is merged with the headers on a per-request basis
80
82
 
81
83
  // Control simple variables
@@ -97,18 +99,54 @@ api.put(url, { body, headers, ... });
97
99
  // ...
98
100
  ```
99
101
 
100
- ### URL
102
+ ### Method
103
+
104
+ The HTTP method to make the request. When using the shorthand, it defaults to `GET`. We recommend using the method syntax:
105
+
106
+ ```js
107
+ import api from 'fch';
108
+
109
+ api.get('/cats');
110
+ api.post('/cats', { body: { name: 'snowball' } });
111
+ api.put(`/cats/3`, { body: { name: 'snowball' }});
112
+ ```
113
+
114
+ You can use it with the plain function as an option parameter. The methods are all lowercase but the option as a parameter is case insensitive; it can be either uppercase or lowercase:
115
+
116
+ ```js
117
+ // Recommended way of dealing with methods:
118
+ api.get(...);
119
+
120
+ // INVALID; won't work
121
+ api.GET(...);
122
+
123
+ // Both of these are valid:
124
+ api({ method; 'GET' })
125
+ api({ method; 'get'})
126
+ ```
127
+
128
+ Example: adding a new cat and fixing a typo:
129
+
130
+ ```js
131
+ import api from 'fch';
132
+
133
+ const cats = await api.get('/cats');
134
+ console.log(cats);
135
+ const { id } = await api.post('/cats', { body: { name: 'snowbll' } });
136
+ await api.put(`/cats/${id}`, { body: { name: 'snowball' }})
137
+ ```
138
+
139
+ ### Url
101
140
 
102
- This is normally the first argument, though technically you can use both styles:
141
+ Specify where to send the request to. It's normally the first argument, though technically you can use both styles:
103
142
 
104
143
  ```js
105
- // All of these methods are valid
106
144
  import api from 'fch';
107
145
 
108
- // We strongly recommend using this style for your normal code:
146
+ // Recommended way of specifying the Url
109
147
  await api.post('/hello', { body: '...', headers: {} })
110
148
 
111
- // Try to avoid these, but they are also valid:
149
+ // These are also valid if you prefer their style; we won't judge
112
150
  await api('/hello', { method: 'post', body: '...', headers: {} });
113
151
  await api({ url: '/hello', method: 'post', headers: {}, body: '...' });
114
152
  await api.post({ url: '/hello', headers: {}, body: '...' });
@@ -123,9 +161,11 @@ api.get('/hello');
123
161
  // Called https//api.filemon.io/hello
124
162
  ```
125
163
 
164
+ > Note: with Node.js you need to either set an absolute baseUrl or make the URL absolute
165
+
126
166
  ### Body
127
167
 
128
- The `body` can be a string, a plain object|array or a FormData instance. If it's an object, it'll be stringified and the header `application/json` will be added. Otherwise it'll be sent as plain text:
168
+ The `body` can be a string, a plain object|array or a FormData instance. If it's an array or object, it'll be stringified and the header `application/json` will be added. Otherwise it'll be sent as plain text:
129
169
 
130
170
  ```js
131
171
  import api from 'api';
@@ -142,6 +182,46 @@ form.onsubmit = e => {
142
182
  };
143
183
  ```
144
184
 
185
+ The methods `GET` and `HEAD` do not accept a body and it'll be ignored.
186
+
187
+ The **response body** will be returned by default as the output of the call:
188
+
189
+ ```js
190
+ const body = await api.get('/cats');
191
+ console.log(body);
192
+ // [{ id: 1, }, ...]
193
+ ```
194
+
195
+ When the server specifies the header `Content-Type` as `application/json`, then we'll attempt to parse the response body and return that as the variable. Otherwise, the plain text will be returned.
196
+
197
+ When the function returns the response (if you set `output: "response"` as an option), then the body can be accessed as `response.body`:
198
+
199
+ ```js
200
+ const response = await api.get('/cats', { output: 'response' });
201
+ console.log(response.body);
202
+ // [{ id: 1, }, ...]
203
+ ```
204
+
205
+
206
+ ### Query
207
+
208
+ You can easily pass GET query parameters by using the option `query`:
209
+
210
+ ```js
211
+ api.get('/cats', { query: { limit: 3 } });
212
+ // /cats?limit=3
213
+ ```
214
+
215
+ While rare, some times you might want to persist a query parameter across requests and always include it; in that case, you can define it globally and it'll be added to every request:
216
+
217
+ ```js
218
+ import api from 'fch';
219
+ api.query.myparam = 'abc';
220
+
221
+ api.get('/cats', { query: { limit: 3 } });
222
+ // /cats?limit=3&myparam=abc
223
+ ```
224
+
145
225
 
146
226
  ### Headers
147
227
 
@@ -150,13 +230,28 @@ You can define headers globally, in which case they'll be added to every request
150
230
 
151
231
  ```js
152
232
  import api from 'fch';
153
- api.headers.abc = 'def';
154
233
 
155
- api.get('/helle', { headers: { ghi: 'jkl' } });
234
+ // Globally, so they are reused across all requests
235
+ api.headers.a = 'b';
236
+
237
+ // With an interceptor, in case you need dynamic headers per-request
238
+ api.before = req => {
239
+ req.headers.c = 'd';
240
+ return req;
241
+ };
242
+
243
+ // Set them for this single request:
244
+ api.get('/hello', { headers: { e: 'f' } });
156
245
  // Total headers on the request:
157
- // { abc: 'def', ghi: 'jkl' }
246
+ // { a: 'b', c: 'd', e: 'f' }
158
247
  ```
159
248
 
249
+ When to use each?
250
+
251
+ - If you need headers shared across all requests, like an API key, then the global one is the best place.
252
+ - When you need to extract them dynamically from somewhere it's better to use the .before() interceptor. An example would be the user Authorization token.
253
+ - When it changes on each request, it's not consistent or it's an one-off, use the option argument.
254
+
160
255
 
161
256
  ### Output
162
257
 
@@ -203,7 +298,7 @@ fch('/a', { dedupe: true }); // [DEFAULT] Dedupes GET requests
203
298
  fch('/a', { dedupe: false }) // All fetch() calls trigger a network call
204
299
  ```
205
300
 
206
- > We do not support deduping other methods right now besides `GET` right now
301
+ > We do not support deduping other methods besides `GET` right now
207
302
 
208
303
  Note that opting out of deduping a request will _also_ make that request not be reusable, see this test for details:
209
304
 
@@ -387,6 +482,24 @@ fch.get('/hello'); // Gets http://localhost:3000/hello (or wherever you are)
387
482
 
388
483
  Note: for server-side (Node.js) usage, you always want to set `baseUrl`.
389
484
 
485
+ ### How to cancel an ongoing request?
486
+
487
+ You can cancel ongoing requests [similarly to native fetch()](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort#examples), by passing it a signal:
488
+
489
+ ```js
490
+ import api from 'fch';
491
+
492
+ const controller = new AbortController();
493
+ const signal = controller.signal;
494
+
495
+ abortButton.addEventListener('click', () => {
496
+ controller.abort();
497
+ console.log('Download aborted');
498
+ });
499
+
500
+ api.get(url, { signal });
501
+ ```
502
+
390
503
  ### What are the differences in Node.js vs Browser?
391
504
 
392
505
  First, we use the native Node.js' fetch() and the browser's native fetch(), so any difference between those also applies to this library. For example, if you were to call `"/"` in the browser it'd refer to the current URL, while in Node.js it'd fail since you need to specify the full URL. Some other places where you might find differences: CORS, cache, etc.
@@ -417,4 +530,4 @@ API size is also strikingly different, with **7.8kb** for Axios and **1.9kb** fo
417
530
  As disadvantages, I can think of two major ones for `fch`:
418
531
 
419
532
  - Requires Node.js 18+, which is the version that includes `fetch()` by default.
420
- - Does not support some more advanced options,
533
+ - Does not support many of the more advanced options, like `onUploadProgress` nor `onDownloadProgress`.