UNPKG

21.3 kBMarkdownView Raw
1<div align="center">
2 <br>
3 <div>
4 <img width="600" height="600" src="media/logo.svg" alt="ky">
5 </div>
6 <p align="center">Huge thanks to <a href="https://lunanode.com"><img src="https://sindresorhus.com/assets/thanks/lunanode-logo.svg" width="170"></a> for sponsoring me!</p>
7 <br>
8 <br>
9 <br>
10 <br>
11</div>
12
13> Ky is a tiny and elegant HTTP client based on the browser [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch)
14
15[![Coverage Status](https://codecov.io/gh/sindresorhus/ky/branch/main/graph/badge.svg)](https://codecov.io/gh/sindresorhus/ky)
16[![](https://badgen.net/bundlephobia/minzip/ky)](https://bundlephobia.com/result?p=ky)
17
18Ky targets [modern browsers](#browser-support) and [Deno](https://github.com/denoland/deno). For older browsers, you will need to transpile and use a [`fetch` polyfill](https://github.com/github/fetch) and [`globalThis` polyfill](https://github.com/es-shims/globalThis). For Node.js, check out [Got](https://github.com/sindresorhus/got). For isomorphic needs (like SSR), check out [`ky-universal`](https://github.com/sindresorhus/ky-universal).
19
20It's just a tiny file with no dependencies.
21
22## Benefits over plain `fetch`
23
24- Simpler API
25- Method shortcuts (`ky.post()`)
26- Treats non-2xx status codes as errors (after redirects)
27- Retries failed requests
28- JSON option
29- Timeout support
30- URL prefix option
31- Instances with custom defaults
32- Hooks
33
34## Install
35
36```
37$ npm install ky
38```
39
40###### Download
41
42- [Normal](https://cdn.jsdelivr.net/npm/ky/index.js)
43- [Minified](https://cdn.jsdelivr.net/npm/ky/index.min.js)
44
45###### CDN
46
47- [jsdelivr](https://www.jsdelivr.com/package/npm/ky)
48- [unpkg](https://unpkg.com/ky)
49
50## Usage
51
52```js
53import ky from 'ky';
54
55const json = await ky.post('https://example.com', {json: {foo: true}}).json();
56
57console.log(json);
58//=> `{data: '🦄'}`
59```
60
61With plain `fetch`, it would be:
62
63```js
64class HTTPError extends Error {}
65
66const response = await fetch('https://example.com', {
67 method: 'POST',
68 body: JSON.stringify({foo: true}),
69 headers: {
70 'content-type': 'application/json'
71 }
72});
73
74if (!response.ok) {
75 throw new HTTPError(`Fetch error: ${response.statusText}`);
76}
77
78const json = await response.json();
79
80console.log(json);
81//=> `{data: '🦄'}`
82```
83
84If you are using [Deno](https://github.com/denoland/deno), import Ky from a URL. For example, using a CDN:
85
86```js
87import ky from 'https://cdn.skypack.dev/ky?dts';
88```
89
90## API
91
92### ky(input, options?)
93
94The `input` and `options` are the same as [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch), with some exceptions:
95
96- The `credentials` option is `same-origin` by default, which is the default in the spec too, but not all browsers have caught up yet.
97- Adds some more options. See below.
98
99Returns a [`Response` object](https://developer.mozilla.org/en-US/docs/Web/API/Response) with [`Body` methods](https://developer.mozilla.org/en-US/docs/Web/API/Body#Methods) added for convenience. So you can, for example, call `ky.get(input).json()` directly without having to await the `Response` first. When called like that, an appropriate `Accept` header will be set depending on the body method used. Unlike the `Body` methods of `window.Fetch`; these will throw an `HTTPError` if the response status is not in the range of `200...299`. Also, `.json()` will return an empty string if the response status is `204` instead of throwing a parse error due to an empty body.
100
101### ky.get(input, options?)
102### ky.post(input, options?)
103### ky.put(input, options?)
104### ky.patch(input, options?)
105### ky.head(input, options?)
106### ky.delete(input, options?)
107
108Sets `options.method` to the method name and makes a request.
109
110When using a `Request` instance as `input`, any URL altering options (such as `prefixUrl`) will be ignored.
111
112#### options
113
114Type: `object`
115
116##### method
117
118Type: `string`\
119Default: `'get'`
120
121HTTP method used to make the request.
122
123Internally, the standard methods (`GET`, `POST`, `PUT`, `PATCH`, `HEAD` and `DELETE`) are uppercased in order to avoid server errors due to case sensitivity.
124
125##### json
126
127Type: `object` and any other value accepted by [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)
128
129Shortcut for sending JSON. Use this instead of the `body` option. Accepts any plain object or value, which will be `JSON.stringify()`'d and sent in the body with the correct header set.
130
131##### searchParams
132
133Type: `string | object<string, string | number | boolean> | Array<Array<string | number | boolean>> | URLSearchParams`\
134Default: `''`
135
136Search parameters to include in the request URL. Setting this will override all existing search parameters in the input URL.
137
138Accepts any value supported by [`URLSearchParams()`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/URLSearchParams).
139
140##### prefixUrl
141
142Type: `string | URL`
143
144A prefix to prepend to the `input` URL when making the request. It can be any valid URL, either relative or absolute. A trailing slash `/` is optional and will be added automatically, if needed, when it is joined with `input`. Only takes effect when `input` is a string. The `input` argument cannot start with a slash `/` when using this option.
145
146Useful when used with [`ky.extend()`](#kyextenddefaultoptions) to create niche-specific Ky-instances.
147
148```js
149import ky from 'ky';
150
151// On https://example.com
152
153const response = await ky('unicorn', {prefixUrl: '/api'});
154//=> 'https://example.com/api/unicorn'
155
156const response2 = await ky('unicorn', {prefixUrl: 'https://cats.com'});
157//=> 'https://cats.com/unicorn'
158```
159
160Notes:
161 - After `prefixUrl` and `input` are joined, the result is resolved against the [base URL](https://developer.mozilla.org/en-US/docs/Web/API/Node/baseURI) of the page (if any).
162 - Leading slashes in `input` are disallowed when using this option to enforce consistency and avoid confusion about how the `input` URL is handled, given that `input` will not follow the normal URL resolution rules when `prefixUrl` is being used, which changes the meaning of a leading slash.
163
164##### retry
165
166Type: `object | number`\
167Default:
168- `limit`: `2`
169- `methods`: `get` `put` `head` `delete` `options` `trace`
170- `statusCodes`: [`408`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) [`413`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/413) [`429`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) [`500`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/500) [`502`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502) [`503`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503) [`504`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/504)
171- `maxRetryAfter`: `undefined`
172
173An object representing `limit`, `methods`, `statusCodes` and `maxRetryAfter` fields for maximum retry count, allowed methods, allowed status codes and maximum [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) time.
174
175If `retry` is a number, it will be used as `limit` and other defaults will remain in place.
176
177If `maxRetryAfter` is set to `undefined`, it will use `options.timeout`. If [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header is greater than `maxRetryAfter`, it will cancel the request.
178
179Delays between retries is calculated with the function `0.3 * (2 ** (retry - 1)) * 1000`, where `retry` is the attempt number (starts from 1).
180
181```js
182import ky from 'ky';
183
184const json = await ky('https://example.com', {
185 retry: {
186 limit: 10,
187 methods: ['get'],
188 statusCodes: [413]
189 }
190}).json();
191```
192
193##### timeout
194
195Type: `number | false`\
196Default: `10000`
197
198Timeout in milliseconds for getting a response. Can not be greater than 2147483647.
199If set to `false`, there will be no timeout.
200
201##### hooks
202
203Type: `object<string, Function[]>`\
204Default: `{beforeRequest: [], beforeRetry: [], afterResponse: []}`
205
206Hooks allow modifications during the request lifecycle. Hook functions may be async and are run serially.
207
208###### hooks.beforeRequest
209
210Type: `Function[]`\
211Default: `[]`
212
213This hook enables you to modify the request right before it is sent. Ky will make no further changes to the request after this. The hook function receives `request` and `options` as arguments. You could, for example, modify the `request.headers` here.
214
215The hook can return a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request) to replace the outgoing request, or return a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) to completely avoid making an HTTP request. This can be used to mock a request, check an internal cache, etc. An **important** consideration when returning a request or response from this hook is that any remaining `beforeRequest` hooks will be skipped, so you may want to only return them from the last hook.
216
217```js
218import ky from 'ky';
219
220const api = ky.extend({
221 hooks: {
222 beforeRequest: [
223 request => {
224 request.headers.set('X-Requested-With', 'ky');
225 }
226 ]
227 }
228});
229
230const response = await api.get('https://example.com/api/users');
231```
232
233###### hooks.beforeRetry
234
235Type: `Function[]`\
236Default: `[]`
237
238This hook enables you to modify the request right before retry. Ky will make no further changes to the request after this. The hook function receives an object with the normalized request and options, an error instance, and the retry count. You could, for example, modify `request.headers` here.
239
240If the request received a response, the error will be of type `HTTPError` and the `Response` object will be available at `error.response`. Be aware that some types of errors, such as network errors, inherently mean that a response was not received. In that case, the error will not be an instance of `HTTPError`.
241
242You can prevent Ky from retrying the request by throwing an error. Ky will not handle it in any way and the error will be propagated to the request initiator. The rest of the `beforeRetry` hooks will not be called in this case. Alternatively, you can return the [`ky.stop`](#ky.stop) symbol to do the same thing but without propagating an error (this has some limitations, see `ky.stop` docs for details).
243
244```js
245import ky from 'ky';
246
247const response = await ky('https://example.com', {
248 hooks: {
249 beforeRetry: [
250 async ({request, options, error, retryCount}) => {
251 const token = await ky('https://example.com/refresh-token');
252 request.headers.set('Authorization', `token ${token}`);
253 }
254 ]
255 }
256});
257```
258
259###### hooks.afterResponse
260
261Type: `Function[]`\
262Default: `[]`
263
264This hook enables you to read and optionally modify the response. The hook function receives normalized request, options, and a clone of the response as arguments. The return value of the hook function will be used by Ky as the response object if it's an instance of [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
265
266```js
267import ky from 'ky';
268
269const response = await ky('https://example.com', {
270 hooks: {
271 afterResponse: [
272 (_request, _options, response) => {
273 // You could do something with the response, for example, logging.
274 log(response);
275
276 // Or return a `Response` instance to overwrite the response.
277 return new Response('A different response', {status: 200});
278 },
279
280 // Or retry with a fresh token on a 403 error
281 async (request, options, response) => {
282 if (response.status === 403) {
283 // Get a fresh token
284 const token = await ky('https://example.com/token').text();
285
286 // Retry with the token
287 request.headers.set('Authorization', `token ${token}`);
288
289 return ky(request);
290 }
291 }
292 ]
293 }
294});
295```
296
297##### throwHttpErrors
298
299Type: `boolean`\
300Default: `true`
301
302Throw an `HTTPError` when, after following redirects, the response has a non-2xx status code. To also throw for redirects instead of following them, set the [`redirect`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) option to `'manual'`.
303
304Setting this to `false` may be useful if you are checking for resource availability and are expecting error responses.
305
306##### onDownloadProgress
307
308Type: `Function`
309
310Download progress event handler.
311
312The function receives a `progress` and `chunk` argument:
313- The `progress` object contains the following elements: `percent`, `transferredBytes` and `totalBytes`. If it's not possible to retrieve the body size, `totalBytes` will be `0`.
314- The `chunk` argument is an instance of `Uint8Array`. It's empty for the first call.
315
316```js
317import ky from 'ky';
318
319const response = await ky('https://example.com', {
320 onDownloadProgress: (progress, chunk) => {
321 // Example output:
322 // `0% - 0 of 1271 bytes`
323 // `100% - 1271 of 1271 bytes`
324 console.log(`${progress.percent * 100}% - ${progress.transferredBytes} of ${progress.totalBytes} bytes`);
325 }
326});
327```
328
329##### parseJson
330
331Type: `Function`\
332Default: `JSON.parse()`
333
334User-defined JSON-parsing function.
335
336Use-cases:
3371. Parse JSON via the [`bourne` package](https://github.com/hapijs/bourne) to protect from prototype pollution.
3382. Parse JSON with [`reviver` option of `JSON.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse).
339
340```js
341import ky from 'ky';
342import bourne from '@hapijs/bourne';
343
344const json = await ky('https://example.com', {
345 parseJson: text => bourne(text)
346}).json();
347```
348
349##### fetch
350
351Type: `Function`\
352Default: `fetch`
353
354User-defined `fetch` function.
355Has to be fully compatible with the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) standard.
356
357Use-cases:
3581. Use custom `fetch` implementations like [`isomorphic-unfetch`](https://www.npmjs.com/package/isomorphic-unfetch).
3592. Use the `fetch` wrapper function provided by some frameworks that use server-side rendering (SSR).
360
361```js
362import ky from 'ky';
363import fetch from 'isomorphic-unfetch';
364
365const json = await ky('https://example.com', {fetch}).json();
366```
367
368### ky.extend(defaultOptions)
369
370Create a new `ky` instance with some defaults overridden with your own.
371
372In contrast to `ky.create()`, `ky.extend()` inherits defaults from its parent.
373
374You can pass headers as a `Headers` instance or a plain object.
375
376You can remove a header with `.extend()` by passing the header with an `undefined` value.
377Passing `undefined` as a string removes the header only if it comes from a `Headers` instance.
378
379```js
380import ky from 'ky';
381
382const url = 'https://sindresorhus.com';
383
384const original = ky.create({
385 headers: {
386 rainbow: 'rainbow',
387 unicorn: 'unicorn'
388 }
389});
390
391const extended = original.extend({
392 headers: {
393 rainbow: undefined
394 }
395});
396
397const response = await extended(url).json();
398
399console.log('rainbow' in response);
400//=> false
401
402console.log('unicorn' in response);
403//=> true
404```
405
406### ky.create(defaultOptions)
407
408Create a new Ky instance with complete new defaults.
409
410```js
411import ky from 'ky';
412
413// On https://my-site.com
414
415const api = ky.create({prefixUrl: 'https://example.com/api'});
416
417const response = await api.get('users/123');
418//=> 'https://example.com/api/users/123'
419
420const response = await api.get('/status', {prefixUrl: ''});
421//=> 'https://my-site.com/status'
422```
423
424#### defaultOptions
425
426Type: `object`
427
428### ky.stop
429
430A `Symbol` that can be returned by a `beforeRetry` hook to stop the retry. This will also short circuit the remaining `beforeRetry` hooks.
431
432Note: Returning this symbol makes Ky abort and return with an `undefined` response. Be sure to check for a response before accessing any properties on it or use [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining). It is also incompatible with body methods, such as `.json()` or `.text()`, because there is no response to parse. In general, we recommend throwing an error instead of returning this symbol, as that will cause Ky to abort and then throw, which avoids these limitations.
433
434A valid use-case for `ky.stop` is to prevent retries when making requests for side effects, where the returned data is not important. For example, logging client activity to the server.
435
436```js
437import ky from 'ky';
438
439const options = {
440 hooks: {
441 beforeRetry: [
442 async ({request, options, error, retryCount}) => {
443 const shouldStopRetry = await ky('https://example.com/api');
444 if (shouldStopRetry) {
445 return ky.stop;
446 }
447 }
448 ]
449 }
450};
451
452// Note that response will be `undefined` in case `ky.stop` is returned.
453const response = await ky.post('https://example.com', options);
454
455// Using `.text()` or other body methods is not suppported.
456const text = await ky('https://example.com', options).text();
457```
458
459### HTTPError
460
461Exposed for `instanceof` checks. The error has a `response` property with the [`Response` object](https://developer.mozilla.org/en-US/docs/Web/API/Response), `request` property with the [`Request` object](https://developer.mozilla.org/en-US/docs/Web/API/Request), and `options` property with normalized options (either passed to `ky` when creating an instance with `ky.create()` or directly when performing the request).
462
463### TimeoutError
464
465The error thrown when the request times out. It has a `request` property with the [`Request` object](https://developer.mozilla.org/en-US/docs/Web/API/Request).
466
467## Tips
468
469### Sending form data
470
471Sending form data in Ky is identical to `fetch`. Just pass a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) instance to the `body` option. The `Content-Type` header will be automatically set to `multipart/form-data`.
472
473```js
474import ky from 'ky';
475
476// `multipart/form-data`
477const formData = new FormData();
478formData.append('food', 'fries');
479formData.append('drink', 'icetea');
480
481const response = await ky.post(url, {body: formData});
482```
483
484If you want to send the data in `application/x-www-form-urlencoded` format, you will need to encode the data with [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams).
485
486```js
487import ky from 'ky';
488
489// `application/x-www-form-urlencoded`
490const searchParams = new URLSearchParams();
491searchParams.set('food', 'fries');
492searchParams.set('drink', 'icetea');
493
494const response = await ky.post(url, {body: searchParams});
495```
496
497### Cancellation
498
499Fetch (and hence Ky) has built-in support for request cancellation through the [`AbortController` API](https://developer.mozilla.org/en-US/docs/Web/API/AbortController). [Read more.](https://developers.google.com/web/updates/2017/09/abortable-fetch)
500
501Example:
502
503```js
504import ky from 'ky';
505
506const controller = new AbortController();
507const {signal} = controller;
508
509setTimeout(() => {
510 controller.abort();
511}, 5000);
512
513try {
514 console.log(await ky(url, {signal}).text());
515} catch (error) {
516 if (error.name === 'AbortError') {
517 console.log('Fetch aborted');
518 } else {
519 console.error('Fetch error:', error);
520 }
521}
522```
523
524## FAQ
525
526#### How do I use this in Node.js?
527
528Check out [`ky-universal`](https://github.com/sindresorhus/ky-universal#faq).
529
530#### How do I use this with a web app (React, Vue.js, etc.) that uses server-side rendering (SSR)?
531
532Check out [`ky-universal`](https://github.com/sindresorhus/ky-universal#faq).
533
534#### How do I test a browser library that uses this?
535
536Either use a test runner that can run in the browser, like Mocha, or use [AVA](https://avajs.dev) with `ky-universal`. [Read more.](https://github.com/sindresorhus/ky-universal#faq)
537
538#### How do I use this without a bundler like Webpack?
539
540Upload the [`index.js`](index.js) file in this repo somewhere, for example, to your website server, or use a CDN version. Then import the file.
541
542```html
543<script type="module">
544import ky from 'https://cdn.jsdelivr.net/npm/ky@latest/index.js';
545
546const json = await ky('https://jsonplaceholder.typicode.com/todos/1').json();
547
548console.log(json.title);
549//=> 'delectus aut autem
550</script>
551```
552
553#### How is it different from [`got`](https://github.com/sindresorhus/got)
554
555See my answer [here](https://twitter.com/sindresorhus/status/1037406558945042432). Got is maintained by the same people as Ky.
556
557#### How is it different from [`axios`](https://github.com/axios/axios)?
558
559See my answer [here](https://twitter.com/sindresorhus/status/1037763588826398720).
560
561#### How is it different from [`r2`](https://github.com/mikeal/r2)?
562
563See my answer in [#10](https://github.com/sindresorhus/ky/issues/10).
564
565#### What does `ky` mean?
566
567It's just a random short npm package name I managed to get. It does, however, have a meaning in Japanese:
568
569> A form of text-able slang, KY is an abbreviation for 空気読めない (kuuki yomenai), which literally translates into “cannot read the air.” It's a phrase applied to someone who misses the implied meaning.
570
571## Browser support
572
573The latest version of Chrome, Firefox, and Safari.
574
575## Node.js support
576
577Polyfill the needed browser globals or just use [`ky-universal`](https://github.com/sindresorhus/ky-universal).
578
579## Related
580
581- [ky-universal](https://github.com/sindresorhus/ky-universal) - Use Ky in both Node.js and browsers
582- [got](https://github.com/sindresorhus/got) - Simplified HTTP requests for Node.js
583- [ky-hooks-change-case](https://github.com/alice-health/ky-hooks-change-case) - Ky hooks to modify cases on requests and responses of objects
584
585## Maintainers
586
587- [Sindre Sorhus](https://github.com/sindresorhus)
588- [Szymon Marczak](https://github.com/szmarczak)
589- [Seth Holladay](https://github.com/sholladay)