@@ -7,16 +7,17 @@ This package:
77+ Uses the modern, standardized ` fetch ` function.
88+ Does ** not** throw on non-OK HTTP responses.
99+ ** Allows to fully type all possible HTTP responses depending on the HTTP status code.**
10+ + Probably the tiniest fetch wrapper you'll ever need.
1011
1112## Does a Non-OK Status Code Warrant an Error?
1213
13- No. Non-OK status codes may communicate things like validation errors, which usually requires that the application
14- * informs* the user about which piece of data is wrong. Why should this logic be inside a ` catch ` block? The fact is,
15- ` try..catch ` would be there as a replacement of branching (using ` if ` or ` switch ` ). This is a very smelly code smell.
14+ The short story is:
1615
17- The second reason is that in most runtimes, unwinding the call stack is costly. Why should we pay a price in
18- performance just to include the code smell of using ` try..catch ` as a branching statement? There is no reason to do
19- such thing.
16+ 1 . Wrappers like ` axios ` or ` ky ` do ` if (!response.ok) throw ... ` , which forces code to ` try..catch ` . This is a code
17+ smell: ` try..catch ` is being used as a branching mechanism.
18+ 2 . The performance drop is huge. [ See this benchmark] ( https://jsperf.app/dogeco ) . Over 40% loss.
19+
20+ [ The issue of fetch wrappers explained in more detail] ( https://webjose.hashnode.dev/the-ugly-truth-all-popular-fetch-wrappers-do-it-wrong )
2021
2122## Quickstart
2223
@@ -39,16 +40,16 @@ do is to add the `authorization` header and the `accept` header to every call.
3940
4041``` typescript
4142// myFetch.ts
42- import { obtainToken } from ' ./magical-auth-stuff.js' ;
43- import { setHeaders , type FetchFnUrl , type FetchFnInit } from ' dr-fetch' ;
43+ import { obtainToken } from " ./magical-auth-stuff.js" ;
44+ import { setHeaders , type FetchFnUrl , type FetchFnInit } from " dr-fetch" ;
4445
4546export function myFetch(url : FetchFnUrl , init ? : FetchFnInit ) {
4647 const token = obtainToken ();
4748 // Make sure there's an object where headers can be added:
4849 init ?? = {};
4950 // With setHeaders(), you can add headers to 'init' with a map, an array of tuples, a Headers
5051 // object or a POJO object.
51- setHeaders (init , { ' Accept' : ' application/json' , ' Authorization' : ` Bearer ${token } ` });
52+ setHeaders (init , { Accept: ' application/json' , Authorization: ` Bearer ${token } ` });
5253 // Finally, do fetch.
5354 return fetch (url , init );
5455}
@@ -95,7 +96,8 @@ export default new DrFetch(myFetch)
9596 ;
9697```
9798
98- > ** NOTE** : The content type can also be matched passing a regular expression instead of a string.
99+ > [ !NOTE]
100+ > The content type can also be matched passing a regular expression instead of a string.
99101
100102Now the fetcher object is ready for use.
101103
@@ -105,7 +107,7 @@ This is the fun part where we can enumerate the various shapes of the body depen
105107
106108``` typescript
107109import type { MyData } from " ./my-datatypes.js" ;
108- import fetcher from ' ./fetcher.js' ;
110+ import fetcher from " ./fetcher.js" ;
109111
110112const response = await fetcher
111113 .for < 200 , MyData []> ()
@@ -173,15 +175,21 @@ function specialFetch(url: FetchFnUrl, init?: FetchFnInit) {
173175 ...
174176}
175177
176- const localFetcher = rootFetcher .clone (true ); // Same data-fetching function, body processors and body typing.
177- const localFetcher = rootFetcher .clone (false ); // Same data-fetching function and body processors. No body typing.
178- const localFetcher = rootFetcher .clone (true , { fetchFn: specialFetch }); // Different data-fetching function.
179- const localFetcher = rootFetcher .clone (true , { includeProcessors: false }); // No custom body processors.
180- const localFetcher = rootFetcher .clone (true , { fetchFn: false }); // Identical processors and body typing, stock fetch().
178+ // Same data-fetching function, body processors and body typing.
179+ const specialFetcher = rootFetcher .clone (true );
180+ // Same data-fetching function and body processors. No body typing.
181+ const specialFetcher = rootFetcher .clone (false );
182+ // Different data-fetching function.
183+ const specialFetcher = rootFetcher .clone (true , { fetchFn: specialFetch });
184+ // No custom body processors.
185+ const specialFetcher = rootFetcher .clone (true , { includeProcessors: false });
186+ // Identical processors and body typing, stock fetch().
187+ const specialFetcher = rootFetcher .clone (true , { fetchFn: false });
181188```
182189
183- > ** IMPORTANT** : The first parameter to the ` clone ` function cannot be a variable. It is just used as a TypeScript
184- > trick to reset the body typing. The value itself means nothing in runtime because types are not a runtime thing.
190+ > [ !IMPORTANT]
191+ > The first parameter to the ` clone ` function cannot be a variable. It is just used as a TypeScript trick to reset the
192+ > body typing. The value itself means nothing in runtime because types are not a runtime thing.
185193
186194## Shortcut Functions
187195
@@ -194,7 +202,7 @@ the `Content-Type` header is given the value `application/json`. If a body of a
194202` fetch() ` (or the custom data-fetching function you provide) does in these cases.
195203
196204``` typescript
197- import type { Todo } from ' ./myTypes.js' ;
205+ import type { Todo } from " ./myTypes.js" ;
198206
199207const newTodo = { text: ' I am new. Insert me!' };
200208const response = await fetcher
@@ -253,7 +261,9 @@ export function myFetch(URL: FetchFnUrl, init?: FetchFnInit) {
253261}
254262```
255263
256- This would also get more complex if you account for multi-value headers. Now the same thing, using ` setHeaders() ` :
264+ This would also get more complex if you account for multi-value headers.
265+
266+ Now the same thing, using ` setHeaders() ` :
257267
258268``` typescript
259269import type { FetchFnUrl , FetchFnInit } from " dr-fetch" ;
@@ -271,8 +281,8 @@ export function myFetch(URL: FetchFnUrl, init?: FetchFnInit) {
271281}
272282```
273283
274- The difference is indeed pretty shocking. Also note that adding arrays of values doesn't increase the complexity of
275- the code.
284+ The difference is indeed pretty shocking: One line of code and you are done . Also note that adding arrays of values
285+ doesn't increase the complexity of the code: It's still one line .
276286
277287### makeIterableHeaders
278288
@@ -300,7 +310,7 @@ const myHeaders4 = [
300310 [' Authorization' , ' Bearer x' ],
301311];
302312
303- // The output of these is identical.
313+ // The output of all these is identical.
304314console .log ([... makeIterableHeaders (myHeaders1 )]);
305315console .log ([... makeIterableHeaders (myHeaders2 )]);
306316console .log ([... makeIterableHeaders (myHeaders3 )]);
@@ -331,6 +341,8 @@ a simple class and a custom body processor.
331341The following is a class for ** Svelte v5** . It contains a reactive ` progress ` property that is updated as download
332342progresses.
333343
344+ [ Live demo in the Svelte REPL] ( https://svelte.dev/playground/ddeedfb44ab74727ac40df320c552b92?version=5.25.3 )
345+
334346> [ !NOTE]
335347> You should have no problems translating this to Vue, SolidJS or even Angular since all these are signal-powered.
336348> For React, you'll have to get rid of the signals part and perhaps make it callback-powered.
@@ -368,7 +380,7 @@ Create a custom processor for the content type that will be received, for exampl
368380
369381``` ts
370382// downloader.ts
371- import { DownloadProgress } from ' ./DownloadProgress.svelte.js' ;
383+ import { DownloadProgress } from " ./DownloadProgress.svelte.js" ;
372384
373385export default new DrFetch (/* custom fetch function here, if needed */ )
374386 .withProcessor (' video/mp4' , (r ) => Promise .resolve (new DownloadProgress (r )))
@@ -380,8 +392,8 @@ carry the class instance in the `body` property.
380392
381393``` svelte
382394<script lang="ts">
383- import { DownloadProgress } from ' ./DownloadProgress.svelte.js' ;
384- import downloader from ' ./downloader.js' ;
395+ import { DownloadProgress } from " ./DownloadProgress.svelte.js" ;
396+ import downloader from " ./downloader.js" ;
385397
386398 let download = $state<Download>();
387399
0 commit comments