UNPKG

18.9 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[![Build Status](https://travis-ci.com/sindresorhus/ky.svg?branch=master)](https://travis-ci.com/sindresorhus/ky) [![codecov](https://codecov.io/gh/sindresorhus/ky/branch/master/graph/badge.svg)](https://codecov.io/gh/sindresorhus/ky) [![](https://badgen.net/bundlephobia/minzip/ky)](https://bundlephobia.com/result?p=ky)
16
17Ky 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). 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).
18
19It's just a tiny file with no dependencies.
20
21## Benefits over plain `fetch`
22
23- Simpler API
24- Method shortcuts (`ky.post()`)
25- Treats non-2xx status codes as errors
26- Retries failed requests
27- JSON option
28- Timeout support
29- URL prefix option
30- Instances with custom defaults
31- Hooks
32
33## Install
34
35```
36$ npm install ky
37```
38
39###### Download
40
41- [Normal](https://cdn.jsdelivr.net/npm/ky/index.js)
42- [Minified](https://cdn.jsdelivr.net/npm/ky/index.min.js)
43
44###### CDN
45
46- [jsdelivr](https://www.jsdelivr.com/package/npm/ky)
47- [unpkg](https://unpkg.com/ky)
48
49## Usage
50
51```js
52import ky from 'ky';
53
54(async () => {
55 const parsed = await ky.post('https://example.com', {json: {foo: true}}).json();
56
57 console.log(parsed);
58 //=> `{data: '🦄'}`
59})();
60```
61
62With plain `fetch`, it would be:
63
64```js
65(async () => {
66 class HTTPError extends Error {}
67
68 const response = await fetch('https://example.com', {
69 method: 'POST',
70 body: JSON.stringify({foo: true}),
71 headers: {
72 'content-type': 'application/json'
73 }
74 });
75
76 if (!response.ok) {
77 throw new HTTPError('Fetch error:', response.statusText);
78 }
79
80 const parsed = await response.json();
81
82 console.log(parsed);
83 //=> `{data: '🦄'}`
84})();
85```
86
87If you are using [Deno](https://github.com/denoland/deno), import Ky from a URL. For example, using a CDN:
88
89```js
90import ky from 'https://unpkg.com/ky/index.js';
91```
92
93In environments that do not support `import`, you can load `ky` in [UMD format](https://medium.freecodecamp.org/anatomy-of-js-module-systems-and-building-libraries-fadcd8dbd0e). For example, using `require()`:
94
95```js
96const ky = require('ky/umd');
97```
98
99With the UMD version, it's also easy to use `ky` [without a bundler](#how-do-i-use-this-without-a-bundler-like-webpack) or module system.
100
101## API
102
103### ky(input, options?)
104
105The `input` and `options` are the same as [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch), with some exceptions:
106
107- The `credentials` option is `same-origin` by default, which is the default in the spec too, but not all browsers have caught up yet.
108- Adds some more options. See below.
109
110Returns 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.
111
112### ky.get(input, options?)
113### ky.post(input, options?)
114### ky.put(input, options?)
115### ky.patch(input, options?)
116### ky.head(input, options?)
117### ky.delete(input, options?)
118
119Sets `options.method` to the method name and makes a request.
120
121When using a `Request` instance as `input`, any URL altering options (such as `prefixUrl`) will be ignored.
122
123#### options
124
125Type: `object`
126
127##### method
128
129Type: `string`\
130Default: `'get'`
131
132HTTP method used to make the request.
133
134Internally, the standard methods (`GET`, `POST`, `PUT`, `PATCH`, `HEAD` and `DELETE`) are uppercased in order to avoid server errors due to case sensitivity.
135
136##### json
137
138Type: `object` and any other value accepted by [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)
139
140Shortcut 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.
141
142##### searchParams
143
144Type: `string | object<string, string | number | boolean> | Array<Array<string | number | boolean>> | URLSearchParams`\
145Default: `''`
146
147Search parameters to include in the request URL. Setting this will override all existing search parameters in the input URL.
148
149Accepts any value supported by [`URLSearchParams()`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/URLSearchParams).
150
151##### prefixUrl
152
153Type: `string | URL`
154
155A 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.
156
157Useful when used with [`ky.extend()`](#kyextenddefaultoptions) to create niche-specific Ky-instances.
158
159```js
160import ky from 'ky';
161
162// On https://example.com
163
164(async () => {
165 await ky('unicorn', {prefixUrl: '/api'});
166 //=> 'https://example.com/api/unicorn'
167
168 await ky('unicorn', {prefixUrl: 'https://cats.com'});
169 //=> 'https://cats.com/unicorn'
170})();
171```
172
173Notes:
174 - 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).
175 - 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.
176
177##### retry
178
179Type: `object | number`\
180Default:
181- `limit`: `2`
182- `methods`: `get` `put` `head` `delete` `options` `trace`
183- `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)
184- `maxRetryAfter`: `undefined`
185
186An 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.
187
188If `retry` is a number, it will be used as `limit` and other defaults will remain in place.
189
190If `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.
191
192Delays between retries is calculated with the function `0.3 * (2 ** (retry - 1)) * 1000`, where `retry` is the attempt number (starts from 1).
193
194```js
195import ky from 'ky';
196
197(async () => {
198 const parsed = await ky('https://example.com', {
199 retry: {
200 limit: 10,
201 methods: ['get'],
202 statusCodes: [413]
203 }
204 }).json();
205})();
206```
207
208##### timeout
209
210Type: `number | false`\
211Default: `10000`
212
213Timeout in milliseconds for getting a response. Can not be greater than 2147483647.
214If set to `false`, there will be no timeout.
215
216##### hooks
217
218Type: `object<string, Function[]>`\
219Default: `{beforeRequest: [], beforeRetry: [], afterResponse: []}`
220
221Hooks allow modifications during the request lifecycle. Hook functions may be async and are run serially.
222
223###### hooks.beforeRequest
224
225Type: `Function[]`\
226Default: `[]`
227
228This 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.
229
230The 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.
231
232```js
233import ky from 'ky';
234
235const api = ky.extend({
236 hooks: {
237 beforeRequest: [
238 request => {
239 request.headers.set('X-Requested-With', 'ky');
240 }
241 ]
242 }
243});
244
245(async () => {
246 const users = await api.get('https://example.com/api/users');
247 // ...
248})();
249```
250
251###### hooks.beforeRetry
252
253Type: `Function[]`\
254Default: `[]`
255
256This 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 the normalized request and options, the failed response, an error instance and the retry count as arguments. You could, for example, modify `request.headers` here.
257
258```js
259import ky from 'ky';
260
261(async () => {
262 await ky('https://example.com', {
263 hooks: {
264 beforeRetry: [
265 async ({request, response, options, errors, retryCount}) => {
266 const token = await ky('https://example.com/refresh-token');
267 request.headers.set('Authorization', `token ${token}`);
268 }
269 ]
270 }
271 });
272})();
273```
274
275###### hooks.afterResponse
276
277Type: `Function[]`\
278Default: `[]`
279
280This 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).
281
282```js
283import ky from 'ky';
284
285(async () => {
286 await ky('https://example.com', {
287 hooks: {
288 afterResponse: [
289 (_request, _options, response) => {
290 // You could do something with the response, for example, logging.
291 log(response);
292
293 // Or return a `Response` instance to overwrite the response.
294 return new Response('A different response', {status: 200});
295 },
296
297 // Or retry with a fresh token on a 403 error
298 async (request, options, response) => {
299 if (response.status === 403) {
300 // Get a fresh token
301 const token = await ky('https://example.com/token').text();
302
303 // Retry with the token
304 request.headers.set('Authorization', `token ${token}`);
305
306 return ky(request);
307 }
308 }
309 ]
310 }
311 });
312})();
313```
314
315##### throwHttpErrors
316
317Type: `boolean`\
318Default: `true`
319
320Throw a `HTTPError` for error responses (non-2xx status codes).
321
322Setting this to `false` may be useful if you are checking for resource availability and are expecting error responses.
323
324##### onDownloadProgress
325
326Type: `Function`
327
328Download progress event handler.
329
330The function receives a `progress` and `chunk` argument:
331- 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`.
332- The `chunk` argument is an instance of `Uint8Array`. It's empty for the first call.
333
334```js
335import ky from 'ky';
336
337(async () => {
338 await ky('https://example.com', {
339 onDownloadProgress: (progress, chunk) => {
340 // Example output:
341 // `0% - 0 of 1271 bytes`
342 // `100% - 1271 of 1271 bytes`
343 console.log(`${progress.percent * 100}% - ${progress.transferredBytes} of ${progress.totalBytes} bytes`);
344 }
345 });
346})();
347```
348
349### ky.extend(defaultOptions)
350
351Create a new `ky` instance with some defaults overridden with your own.
352
353In contrast to `ky.create()`, `ky.extend()` inherits defaults from its parent.
354
355You can pass headers as a `Headers` instance or a plain object.
356
357You can remove a header with `.extend()` by passing the header with an `undefined` value.
358Passing `undefined` as a string removes the header only if it comes from a `Headers` instance.
359
360```js
361import ky from 'ky';
362
363const url = 'https://sindresorhus.com';
364
365const original = ky.create({
366 headers: {
367 rainbow: 'rainbow',
368 unicorn: 'unicorn'
369 }
370});
371
372const extended = original.extend({
373 headers: {
374 rainbow: undefined
375 }
376});
377
378const response = await extended(url).json();
379
380console.log('rainbow' in response);
381//=> false
382
383console.log('unicorn' in response);
384//=> true
385```
386
387### ky.create(defaultOptions)
388
389Create a new Ky instance with complete new defaults.
390
391```js
392import ky from 'ky';
393
394// On https://my-site.com
395
396const api = ky.create({prefixUrl: 'https://example.com/api'});
397
398(async () => {
399 await api.get('users/123');
400 //=> 'https://example.com/api/users/123'
401
402 await api.get('/status', {prefixUrl: ''});
403 //=> 'https://my-site.com/status'
404})();
405```
406
407#### defaultOptions
408
409Type: `object`
410
411### ky.HTTPError
412
413Exposed for `instanceof` checks. The error has a `response` property with the [`Response` object](https://developer.mozilla.org/en-US/docs/Web/API/Response).
414
415### ky.TimeoutError
416
417The error thrown when the request times out.
418
419### ky.stop
420
421A `Symbol` that can be returned by a `beforeRetry` hook to stop the retry. This will also short circuit the remaining `beforeRetry` hooks.
422
423```js
424import ky from 'ky';
425
426(async () => {
427 await ky('https://example.com', {
428 hooks: {
429 beforeRetry: [
430 async ({request, response, options, errors, retryCount}) => {
431 const shouldStopRetry = await ky('https://example.com/api');
432 if (shouldStopRetry) {
433 return ky.stop;
434 }
435 }
436 ]
437 }
438 });
439})();
440```
441
442## Tips
443
444### Sending form data
445
446Sending 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`.
447
448```js
449import ky from 'ky';
450
451(async () => {
452 // `multipart/form-data`
453 const formData = new FormData();
454 formData.append('food', 'fries');
455 formData.append('drink', 'icetea');
456
457 await ky.post(url, {
458 body: formData
459 });
460})();
461```
462
463If 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).
464
465```js
466import ky from 'ky';
467
468(async () => {
469 // `application/x-www-form-urlencoded`
470 const searchParams = new URLSearchParams();
471 searchParams.set('food', 'fries');
472 searchParams.set('drink', 'icetea');
473
474 await ky.post(url, {
475 body: searchParams
476 });
477})();
478```
479
480### Cancellation
481
482Fetch (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)
483
484Example:
485
486```js
487import ky from 'ky';
488
489const controller = new AbortController();
490const {signal} = controller;
491
492setTimeout(() => {
493 controller.abort();
494}, 5000);
495
496(async () => {
497 try {
498 console.log(await ky(url, {signal}).text());
499 } catch (error) {
500 if (error.name === 'AbortError') {
501 console.log('Fetch aborted');
502 } else {
503 console.error('Fetch error:', error);
504 }
505 }
506})();
507```
508
509## FAQ
510
511#### How do I use this in Node.js?
512
513Check out [`ky-universal`](https://github.com/sindresorhus/ky-universal#faq).
514
515#### How do I use this with a web app (React, Vue.js, etc.) that uses server-side rendering (SSR)?
516
517Check out [`ky-universal`](https://github.com/sindresorhus/ky-universal#faq).
518
519#### How do I test a browser library that uses this?
520
521Either use a test runner that can run in the browser, like Mocha, or use [AVA](http://ava.li) with `ky-universal`. [Read more.](https://github.com/sindresorhus/ky-universal#faq)
522
523#### How do I use this without a bundler like Webpack?
524
525Upload 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.
526
527```html
528<script type="module">
529import ky from 'https://cdn.jsdelivr.net/npm/ky@latest/index.js';
530
531(async () => {
532 const parsed = await ky('https://jsonplaceholder.typicode.com/todos/1').json();
533
534 console.log(parsed.title);
535 //=> 'delectus aut autem
536})();
537</script>
538```
539
540Alternatively, you can use the [`umd.js`](umd.js) file with a traditional `<script>` tag (without `type="module"`), in which case `ky` will be a global.
541
542```html
543<script src="https://cdn.jsdelivr.net/npm/ky@latest/umd.js"></script>
544<script>
545(async () => {
546 const parsed = await ky('https://jsonplaceholder.typicode.com/todos/1').json();
547
548 console.log(parsed.title);
549 //=> 'delectus aut autem
550})();
551</script>
552```
553
554#### How is it different from [`got`](https://github.com/sindresorhus/got)
555
556See my answer [here](https://twitter.com/sindresorhus/status/1037406558945042432). Got is maintained by the same people as Ky.
557
558#### How is it different from [`axios`](https://github.com/axios/axios)?
559
560See my answer [here](https://twitter.com/sindresorhus/status/1037763588826398720).
561
562#### How is it different from [`r2`](https://github.com/mikeal/r2)?
563
564See my answer in [#10](https://github.com/sindresorhus/ky/issues/10).
565
566#### What does `ky` mean?
567
568It's just a random short npm package name I managed to get. It does, however, have a meaning in Japanese:
569
570> 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.
571
572## Browser support
573
574The latest version of Chrome, Firefox, and Safari.
575
576## Node.js support
577
578Polyfill the needed browser global or just use [`ky-universal`](https://github.com/sindresorhus/ky-universal).
579
580## Related
581
582- [ky-universal](https://github.com/sindresorhus/ky-universal) - Use Ky in both Node.js and browsers
583- [got](https://github.com/sindresorhus/got) - Simplified HTTP requests for Node.js
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)