🌳 Tiny & elegant JavaScript HTTP client based on the browser Fetch API

Overview

ky

Huge thanks to for sponsoring me!





Ky is a tiny and elegant HTTP client based on the browser Fetch API

Coverage Status

Ky targets modern browsers and Deno. For older browsers, you will need to transpile and use a fetch polyfill and globalThis polyfill. For Node.js, check out Got. For isomorphic needs (like SSR), check out ky-universal.

It's just a tiny file with no dependencies.

Benefits over plain fetch

  • Simpler API
  • Method shortcuts (ky.post())
  • Treats non-2xx status codes as errors (after redirects)
  • Retries failed requests
  • JSON option
  • Timeout support
  • URL prefix option
  • Instances with custom defaults
  • Hooks

Install

$ npm install ky
Download
CDN

Usage

import ky from 'ky';

const json = await ky.post('https://example.com', {json: {foo: true}}).json();

console.log(json);
//=> `{data: '🦄'}`

With plain fetch, it would be:

class HTTPError extends Error {}

const response = await fetch('https://example.com', {
	method: 'POST',
	body: JSON.stringify({foo: true}),
	headers: {
		'content-type': 'application/json'
	}
});

if (!response.ok) {
	throw new HTTPError(`Fetch error: ${response.statusText}`);
}

const json = await response.json();

console.log(json);
//=> `{data: '🦄'}`

If you are using Deno, import Ky from a URL. For example, using a CDN:

import ky from 'https://cdn.skypack.dev/ky?dts';

API

ky(input, options?)

The input and options are the same as fetch, with some exceptions:

  • The credentials option is same-origin by default, which is the default in the spec too, but not all browsers have caught up yet.
  • Adds some more options. See below.

Returns a Response object with 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.

ky.get(input, options?)

ky.post(input, options?)

ky.put(input, options?)

ky.patch(input, options?)

ky.head(input, options?)

ky.delete(input, options?)

Sets options.method to the method name and makes a request.

When using a Request instance as input, any URL altering options (such as prefixUrl) will be ignored.

options

Type: object

method

Type: string
Default: 'get'

HTTP method used to make the request.

Internally, the standard methods (GET, POST, PUT, PATCH, HEAD and DELETE) are uppercased in order to avoid server errors due to case sensitivity.

json

Type: object and any other value accepted by JSON.stringify()

Shortcut 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.

searchParams

Type: string | object<string, string | number | boolean> | Array<Array<string | number | boolean>> | URLSearchParams
Default: ''

Search parameters to include in the request URL. Setting this will override all existing search parameters in the input URL.

Accepts any value supported by URLSearchParams().

prefixUrl

Type: string | URL

A 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.

Useful when used with ky.extend() to create niche-specific Ky-instances.

import ky from 'ky';

// On https://example.com

const response = await ky('unicorn', {prefixUrl: '/api'});
//=> 'https://example.com/api/unicorn'

const response2 = await ky('unicorn', {prefixUrl: 'https://cats.com'});
//=> 'https://cats.com/unicorn'

Notes:

  • After prefixUrl and input are joined, the result is resolved against the base URL of the page (if any).
  • 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.
retry

Type: object | number
Default:

  • limit: 2
  • methods: get put head delete options trace
  • statusCodes: 408 413 429 500 502 503 504
  • maxRetryAfter: undefined

An object representing limit, methods, statusCodes and maxRetryAfter fields for maximum retry count, allowed methods, allowed status codes and maximum Retry-After time.

If retry is a number, it will be used as limit and other defaults will remain in place.

If maxRetryAfter is set to undefined, it will use options.timeout. If Retry-After header is greater than maxRetryAfter, it will cancel the request.

Delays between retries is calculated with the function 0.3 * (2 ** (retry - 1)) * 1000, where retry is the attempt number (starts from 1).

import ky from 'ky';

const json = await ky('https://example.com', {
	retry: {
		limit: 10,
		methods: ['get'],
		statusCodes: [413]
	}
}).json();
timeout

Type: number | false
Default: 10000

Timeout in milliseconds for getting a response. Can not be greater than 2147483647. If set to false, there will be no timeout.

hooks

Type: object<string, Function[]>
Default: {beforeRequest: [], beforeRetry: [], afterResponse: []}

Hooks allow modifications during the request lifecycle. Hook functions may be async and are run serially.

hooks.beforeRequest

Type: Function[]
Default: []

This 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.

The hook can return a Request to replace the outgoing request, or return a 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.

import ky from 'ky';

const api = ky.extend({
	hooks: {
		beforeRequest: [
			request => {
				request.headers.set('X-Requested-With', 'ky');
			}
		]
	}
});

const response = await api.get('https://example.com/api/users');
hooks.beforeRetry

Type: Function[]
Default: []

This 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.

If 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.

You 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 symbol to do the same thing but without propagating an error (this has some limitations, see ky.stop docs for details).

import ky from 'ky';

const response = await ky('https://example.com', {
	hooks: {
		beforeRetry: [
			async ({request, options, error, retryCount}) => {
				const token = await ky('https://example.com/refresh-token');
				request.headers.set('Authorization', `token ${token}`);
			}
		]
	}
});
hooks.afterResponse

Type: Function[]
Default: []

This 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.

import ky from 'ky';

const response = await ky('https://example.com', {
	hooks: {
		afterResponse: [
			(_request, _options, response) => {
				// You could do something with the response, for example, logging.
				log(response);

				// Or return a `Response` instance to overwrite the response.
				return new Response('A different response', {status: 200});
			},

			// Or retry with a fresh token on a 403 error
			async (request, options, response) => {
				if (response.status === 403) {
					// Get a fresh token
					const token = await ky('https://example.com/token').text();

					// Retry with the token
					request.headers.set('Authorization', `token ${token}`);

					return ky(request);
				}
			}
		]
	}
});
throwHttpErrors

Type: boolean
Default: true

Throw 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 option to 'manual'.

Setting this to false may be useful if you are checking for resource availability and are expecting error responses.

onDownloadProgress

Type: Function

Download progress event handler.

The function receives a progress and chunk argument:

  • 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.
  • The chunk argument is an instance of Uint8Array. It's empty for the first call.
import ky from 'ky';

const response = await ky('https://example.com', {
	onDownloadProgress: (progress, chunk) => {
		// Example output:
		// `0% - 0 of 1271 bytes`
		// `100% - 1271 of 1271 bytes`
		console.log(`${progress.percent * 100}% - ${progress.transferredBytes} of ${progress.totalBytes} bytes`);
	}
});
parseJson

Type: Function
Default: JSON.parse()

User-defined JSON-parsing function.

Use-cases:

  1. Parse JSON via the bourne package to protect from prototype pollution.
  2. Parse JSON with reviver option of JSON.parse().
import ky from 'ky';
import bourne from '@hapijs/bourne';

const json = await ky('https://example.com', {
	parseJson: text => bourne(text)
}).json();
fetch

Type: Function
Default: fetch

User-defined fetch function. Has to be fully compatible with the Fetch API standard.

Use-cases:

  1. Use custom fetch implementations like isomorphic-unfetch.
  2. Use the fetch wrapper function provided by some frameworks that use server-side rendering (SSR).
import ky from 'ky';
import fetch from 'isomorphic-unfetch';

const json = await ky('https://example.com', {fetch}).json();

ky.extend(defaultOptions)

Create a new ky instance with some defaults overridden with your own.

In contrast to ky.create(), ky.extend() inherits defaults from its parent.

You can pass headers as a Headers instance or a plain object.

You can remove a header with .extend() by passing the header with an undefined value. Passing undefined as a string removes the header only if it comes from a Headers instance.

import ky from 'ky';

const url = 'https://sindresorhus.com';

const original = ky.create({
	headers: {
		rainbow: 'rainbow',
		unicorn: 'unicorn'
	}
});

const extended = original.extend({
	headers: {
		rainbow: undefined
	}
});

const response = await extended(url).json();

console.log('rainbow' in response);
//=> false

console.log('unicorn' in response);
//=> true

ky.create(defaultOptions)

Create a new Ky instance with complete new defaults.

import ky from 'ky';

// On https://my-site.com

const api = ky.create({prefixUrl: 'https://example.com/api'});

const response = await api.get('users/123');
//=> 'https://example.com/api/users/123'

const response = await api.get('/status', {prefixUrl: ''});
//=> 'https://my-site.com/status'

defaultOptions

Type: object

ky.HTTPError

Exposed for instanceof checks. The error has a response property with the Response object, request property with the Request object, and options property with normalized options (either passed to ky when creating an instance with ky.create() or directly when performing the request).

ky.TimeoutError

The error thrown when the request times out. It has a request property with the Request object.

ky.stop

A Symbol that can be returned by a beforeRetry hook to stop the retry. This will also short circuit the remaining beforeRetry hooks.

Note: 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. 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.

A 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.

import ky from 'ky';

const options = {
	hooks: {
		beforeRetry: [
			async ({request, options, error, retryCount}) => {
				const shouldStopRetry = await ky('https://example.com/api');
				if (shouldStopRetry) {
					return ky.stop;
				}
			}
		]
	}
};

// Note that response will be `undefined` in case `ky.stop` is returned.
const response = await ky.post('https://example.com', options);

// Using `.text()` or other body methods is not suppported.
const text = await ky('https://example.com', options).text();

Tips

Sending form data

Sending form data in Ky is identical to fetch. Just pass a FormData instance to the body option. The Content-Type header will be automatically set to multipart/form-data.

import ky from 'ky';

// `multipart/form-data`
const formData = new FormData();
formData.append('food', 'fries');
formData.append('drink', 'icetea');

const response = await ky.post(url, {body: formData});

If you want to send the data in application/x-www-form-urlencoded format, you will need to encode the data with URLSearchParams.

import ky from 'ky';

// `application/x-www-form-urlencoded`
const searchParams = new URLSearchParams();
searchParams.set('food', 'fries');
searchParams.set('drink', 'icetea');

const response = await ky.post(url, {body: searchParams});

Cancellation

Fetch (and hence Ky) has built-in support for request cancellation through the AbortController API. Read more.

Example:

import ky from 'ky';

const controller = new AbortController();
const {signal} = controller;

setTimeout(() => {
	controller.abort();
}, 5000);

try {
	console.log(await ky(url, {signal}).text());
} catch (error) {
	if (error.name === 'AbortError') {
		console.log('Fetch aborted');
	} else {
		console.error('Fetch error:', error);
	}
}

FAQ

How do I use this in Node.js?

Check out ky-universal.

How do I use this with a web app (React, Vue.js, etc.) that uses server-side rendering (SSR)?

Check out ky-universal.

How do I test a browser library that uses this?

Either use a test runner that can run in the browser, like Mocha, or use AVA with ky-universal. Read more.

How do I use this without a bundler like Webpack?

Upload the index.js file in this repo somewhere, for example, to your website server, or use a CDN version. Then import the file.

<script type="module">
import ky from 'https://cdn.jsdelivr.net/npm/ky@latest/index.js';

const json = await ky('https://jsonplaceholder.typicode.com/todos/1').json();

console.log(json.title);
//=> 'delectus aut autem
</script>

How is it different from got

See my answer here. Got is maintained by the same people as Ky.

How is it different from axios?

See my answer here.

How is it different from r2?

See my answer in #10.

What does ky mean?

It's just a random short npm package name I managed to get. It does, however, have a meaning in Japanese:

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.

Browser support

The latest version of Chrome, Firefox, and Safari.

Node.js support

Polyfill the needed browser globals or just use ky-universal.

Related

  • ky-universal - Use Ky in both Node.js and browsers
  • got - Simplified HTTP requests for Node.js
  • ky-hooks-change-case - Ky hooks to modify cases on requests and responses of objects

Maintainers

Comments
  • Add `onDownloadProgress` option

    Add `onDownloadProgress` option

    Here's the new PR for the potential stream API.

    Basic example for reading an image stream:

    function streamProgressCallback(percentage, bytes) {
         console.log('percentage:: ' + percentage)
         console.log('bytes:: ' + bytes)
    }
    
    const res = await ky.get(someImageURL, { stream: true, streamProgressCallback }).blob();
    
    const imgObject = URL.createObjectURL(res);
    const img = document.createElement('img');
    img.src = img1;
    document.body.appendChild(img)
    

    Not sure how to test streams.


    Fixes #3 Fixes #6

    opened by dangdennis 47
  • Ky typescript, jest and es modules + temp solution

    Ky typescript, jest and es modules + temp solution

    Update from Ky team: Please see Jest's ESM instructions


    Spend few hours trying to make typescript, jest, lerna and ky to play well together. Here's something that might help in case of:

        export default createInstance();
        ^^^^^^
        SyntaxError: Unexpected token export
        > 4 | import ky from "ky";
    

    With Jest you can simply add it to the transformIgnorePatterns, but the way lerna works your node_modules will probably end up in the top level node_modules. So here's something that seems to works in every cases:

        moduleNameMapper: {
            '^ky$': require.resolve('ky').replace('index.js', 'umd.js'),
        }
    

    This way, jest choose the umd build instead of es one.

    Warning, this assumes there will always be an umd.js build packaged with ky.

    There might be better solutions, feel free to let me know yours

    opened by belgattitude 30
  • Add `prefixUrl` option

    Add `prefixUrl` option

    Fixes #1

    ky('world', {
        baseUrl : 'https://mysite.com/hello/'
    });
    

    Note that my implementation is a little bit different than the way it works in got. We can change it, but I figured I'd show this other way first and hear your thoughts.

    input is resolved against baseUrl, which is in turn resolved against the document's base URL.

    This is how I expect an option called baseUrl to behave, especially in the browser where such terminology is already built into the web platform. This isn't as much of a problem for got since there is no equivalent to the <base> tag in Node.js.

    Would you rather:

    • Copy the got semantics and name, which is fine but potentially a bit confusing for people accustomed to how URLs resolve against base URLs in the browser
    • Copy the got semantics but name it something like prefixUrl to reflect the nature of what that does
    • Resolve the URLs as I've implemented it and let this module diverge a little bit from the semantics of got
    opened by sholladay 29
  • Move to TypeScript

    Move to TypeScript

    Following #321, I took some time today to attempt a port to TypeScript with minimal changes.

    Work is not fully done yet (did not update the CI pipeline, hence the failed tests), but it does pass typescript validation.

    There is a two places where I had to @ts-expect-error, related to dynamically assigned props as I'm not sure yet how I can port it to Typescript (could be done easily by assigning each props without the loop, see reviews). Open to ideas on this.

    Also there is some remaining typing issues (existing in the current code but invisible) regarding the actual options passed around (see InternalOptions vs NormalizedOptions) so I had to add a few manual as casting as well that should be fixed (but could be done after the migration to keep changes to a minimum).

    Also I had to refactor the Ky class API a bit (added a static Ky.create) that did return a ResponsePromise from its constructor instead of the expected Ky instance as it looks like TypeScript forbids class constructor return type overloads.

    I tried my best to follow what was made for got and to not change anything in the code yet (I have quite a few changes that I'd like to propose next but that were out of scope).

    Before I spend too much more time on this I'd love to get some feedback on the direction of this merge request and the probability of it being merged once ready.

    I'm willing to change anything that you would not like (naming, etc.).

    Thanks for the review!

    opened by mgcrea 26
  • Why node >= 10 is required ?

    Why node >= 10 is required ?

    Hello, After creating an app using react-create-app, I added ky using yarn add ky. I got this error:

    error [email protected]: The engine "node" is incompatible with this module. Expected version ">=10".
    error Found incompatible module
    

    (with my current nodejs version === 8).

    So I wondering why node 10 is required to install the package ?

    Thanks

    opened by titouancreach 21
  • Module not found: Can't resolve 'ky' with version 0.28

    Module not found: Can't resolve 'ky' with version 0.28

    Upgrading to [email protected], i get Module not found: Can't resolve 'ky'.

    When i open vs code and look at the 'ky' module it refers to index.d.ts and load typescript definition instead of index.js.

    I use create-react-app i don't know if that help you but it works perfectly fine with [email protected]

    bug 
    opened by LoiKos 18
  • Set headers after ky declared

    Set headers after ky declared

    Hello,

    I have a quick question, I tried different options.

    I have a service clientWeb.service.js :

    import ky 	from 'ky';
    
    function authHeader() {
    	/* return authorization header with jwt token */
    	let user = JSON.parse(localStorage.getItem('user'));
    
    	if (user && user.token) {
    		return { 'x-access-token': user.token };
    	} else {
    		return {};
    	}
    }
    
    const clientWeb = ky.extend({
    	prefixUrl: process.env.NODE_ENV === 'development' ? 'http://localhost:3000' : 'https://xxx.xxx.xxx',
    	headers: authHeader()
    });
    
    export { clientWeb }
    

    By default, headers can be set to null if the user is not connected. Once the user is connected, I want to set my headers directly to my clientWeb. I tried several things but nothing worked.

    Here is my authentication.module.js

    async login({ commit }, { email, password }) {
    			try {
    				commit('loginRequest', { email });
    				
    				let user = await userService.login(email, password);
    
    				commit('loginSuccess', user);
    
    				console.log(user);
    				let headers = { 'x-access-token': user.token };
    
    				console.log(clientWeb);
    
    				clientWeb.headers = headers;
    				clientWeb['headers'] = headers;
    				// clientWeb({
    					// headers: headers
    				// });
    
    				router.push('/dashboard');
    			} catch (err) {
    				console.log('error login failure: ' + err);
    				commit('loginFailure', err);
    			}
    		},
    
    opened by edeuxk 17
  • Unhandled Rejection (Error): `input` must not begin with a slash when using `prefixUrl`

    Unhandled Rejection (Error): `input` must not begin with a slash when using `prefixUrl`

    Hi,

    I receive this exception when trying to call an API resource at https://example.com/foo/id

    My ky instance is created as follow:

    import ky from 'ky';
    
    const config = {
        prefixUrl: process.env.API_ENTRYPOINT,
        headers: {
            'accept': 'application/ld+json'
        }
    };
    
    export default ky.extend(config);
    

    I read the discussion related to the #11 and especially the one you wrote https://github.com/sindresorhus/ky/pull/11#issuecomment-424838175

    Sorry to be rude, but I disagree with the choice you made. From my point of view, the only acceptable format should be await ky('/unicorn', {prefixUrl: 'https://cats.com'});.

    It is not a matter of taste, just a choice guided by the RFC3986: As per the section 3.3:

    If a URI contains an authority component, then the path component must either be empty or begin with a slash ("/") character.

    For the url https://cats.com/unicorn we have the following parts:

    • The scheme: https
    • The : separator
    • The authority //cats.com (no userinfo, no port and host=cats.com)
    • The path to the resource: /unicorn

    Except if the path is empty, a path must start with a slash.

    Can you please reconsider the choice you made in #11 and modify this library according to that RFC?

    invalid 
    opened by Spomky 17
  • Include a UMD build

    Include a UMD build

    I'm having to transpile the module before usage and I'm not sure if that's intentional.

    This is the line which caused me pain.

    https://github.com/sindresorhus/ky/blob/eb4b341b8dac088ca6ec436eedc536d101c4b02c/index.js#L192

    Feel free to close this is if the use of export is intentional, I couldn't really tell from the docs.

    help wanted 
    opened by ifiokjr 17
  • POST request doesn't have body for Tizen 2017

    POST request doesn't have body for Tizen 2017

    I have options like this one:

    
    const kyOptions = {
        headers: {
            'Authorization': "Bearer <token>",
        },
        json: {
            content_id: "<id>"
            content_type: "<type>"
        },
        method: "post"
    };
    

    Then I call ky:

    const response = ky(uri, kyOptions);
    

    And I have error as somehow fetch was called without body. If I replace the call with fetch itself, the request will be sent correctly as expected:

    const response = await fetch(uri, {
       body: JSON.stringify(kyOptions.json),
       headers: {
          'Content-Type': 'application/json',
          ...kyOptions.headers,
       },
       method: 'POST'
    });
    

    Do you know why this can happen?

    I use whatwg-fetch and core-js, maybe also something is missed?

    bug help wanted 
    opened by Beraliv 15
  • Add an ES5 build for older browsers

    Add an ES5 build for older browsers

    I've read many similar closed issues, but haven't found a good documented solution on what can I do as a consumer of ky package if I need to support e.g iOS 10.0 browser (that breaks on async/await support currently).

    I agree that it's not related to ky itself, but can be a nice help for the consumers of the package.

    In our particular can we don't use babel (we use typescript), so I'm not sure what's the easiest way to transpile it without complicating our build step with babel and more tools.

    I promise to post a solution here if I stumble upon one myself.

    enhancement good first issue 
    opened by kirillgroshkov 14
  • Using

    Using "Retry-After" with error code 425 is impossible

    Hi there,

    I would like to change the way the retries are made. For example: wait 1s before each retry.

    One option is to add a retry.backoffLimit option Another (by the time the PR is merged) would be to use the Retry-After header from the server. But unfortunately I can make it work with the 425 error code given by the server. It looks like the afterStatusCodes option, set by default to [413, 429, 503] cannot be changed. I've tried these retry options :

    retry: {
      limit: 12,
      afterStatusCodes: [425],
      statusCodes: [425],
    }
    

    The code I'm looking at is here: (Ky.ts) _calculateRetryDelay l.217

    afterStatusCodes is not in the docs… but I found it reading the code and in that article

    Any help is welcome

    opened by yaaax 0
  • `DOMException` returned from `composeAbortError` does not have name AbortError

    `DOMException` returned from `composeAbortError` does not have name AbortError

    Description

    After upgrading to ky 0.32, errors thrown as a result from calling abortController.abort does no longer have name "AbortError", just "Error", making it hard to distinguish between AbortErrors and other errors. This seems to be caused by the fact that composeAbortError does not provide a second parameter name when constructing a new DOMException: https://github.com/sindresorhus/ky/blob/e791d16b27549e89134e446a90bf1b8307749ef0/source/errors/DOMException.ts#L7

    According to MDN, promises rejected as a result of calling abort should have the name AbortError.

    When abort() is called, the fetch() promise rejects with an Error of type DOMException, with name AbortError.

    I may be missing something here – but shouldn't the DOMException created in composeAbortError be given the name AbortError?

    return new DOMException(signal?.reason ?? 'The operation was aborted.', 'AbortError')
    
    opened by janerikbr 0
  • `ky(…).text()` returns stringified response object

    `ky(…).text()` returns stringified response object

    I'm not yet sure of the specific repro steps, but it appears to be related to calling .text() on a response with an empty body. I noticed in the live example where it's happening, there are 2 incorrect response headers, which may be confusing ky: content-length: 574 and content-type: application/json (neither should be present since the response body is intentionally empty). But I can't think what would cause ky to return the response object instead.

    I confirmed this is not an underlying issue with fetch/Response:

    (new Response()).text().then(console.log); // Promise{<fulfilled>: undefined}
    

    I would expect ky(…).text() on an empty response body to return either an empty string or undefined.

    opened by JakobJingleheimer 1
  • 0.32.2 breaking on Cloudflare Workers

    0.32.2 breaking on Cloudflare Workers

    ky 0.31.2 seems to have a breaking change that causes it to not work on Cloudflare Workers:

    Uncaught Error: To use the new ReadableStream() constructor, enable the
      streams_enable_constructors feature flag.
        at index.js:2678:13
        at index.js:2687:3
    

    Tried searching through the build output, and found that in v0.31.1 there are 4 results for ReadableStream, while v0.31.2 has 7

    opened by probablykasper 2
  • response.body is a readable stream instead of response body

    response.body is a readable stream instead of response body

    Documentation States:

    hooks: {
    		beforeError: [
    			error => {
    				const {response} = error;
    				if (response && response.body) {
    					error.name = 'GitHubError';
    					error.message = `${response.body.message} (${response.statusCode})`;
    				}
    
    				return error;
    			}
    		]
    	}
    

    but the response.body is a readable stream.

            beforeError: [
                (error) => {
                    const { response } = error;
                    if(response && response.body) {
                        console.log('this is a readable stream', response.body)
                        error.name = response.statusCode;
                    }
                    return error;
                }
            ]
    

    I need to be able to get the response body for failed calls, to render error message that are set on the backend via a json api. The only way to do this currently is to set the throwHttpErrors to false AND not use .json, which leads the code to have to check returned objects responses as a whole instead of as a json, and to check for specific fields etc to determine if its in error.

    opened by rlwhatley3 3
  • Improve documentation and create a website

    Improve documentation and create a website

    Hi

    I really love this library and I can get my way around the source to understand things.

    But I really think we could improve the documention and include some examples. And maybe create a website.

    I would love to contribute with this since I have a lot of experience on writing documentation and websites for it.

    Do you think this is a good idea? Would be possible for me to work on this?

    opened by zeyuri 2
Releases(v0.33.1)
  • v0.33.1(Dec 29, 2022)

  • v0.33.0(Dec 12, 2022)

  • v0.32.2(Nov 1, 2022)

  • v0.32.1(Oct 31, 2022)

    • Don't return empty string for .json() if Transfer-Encoding is chunked (#464) 195e0e2

    https://github.com/sindresorhus/ky/compare/v0.32.0...v0.32.1

    Source code(tar.gz)
    Source code(zip)
  • v0.32.0(Oct 31, 2022)

    • Parse empty response bodies without throwing when using .json() (#459) 1cc6bbb
    • Fix abortions between retries (#433) dddf7ba

    https://github.com/sindresorhus/ky/compare/v0.31.4...v0.32.0

    Source code(tar.gz)
    Source code(zip)
  • v0.31.4(Oct 12, 2022)

  • v0.31.3(Sep 3, 2022)

  • v0.31.2(Sep 2, 2022)

  • v0.31.1(Jul 21, 2022)

    • Fix copying response body with 204 status code when using onDownloadProgress (#444) d48ed95

    https://github.com/sindresorhus/ky/compare/v0.31.0...v0.31.1

    Source code(tar.gz)
    Source code(zip)
  • v0.31.0(Jun 23, 2022)

    Breaking

    • Require Node.js 14 when used in Node.js e557973

    Improvements

    • Add typed JSON response (#437) cff4c7a
    • Export BeforeRetryState type (#442) e4077b9

    Fixes

    • Fix response details getting lost when using onDownloadProgress (#441) f34fb1f

    https://github.com/sindresorhus/ky/compare/v0.30.0...v0.31.0

    Source code(tar.gz)
    Source code(zip)
  • v0.30.0(Mar 2, 2022)

  • v0.29.0(Feb 15, 2022)

  • v0.28.7(Nov 8, 2021)

  • v0.28.6(Oct 13, 2021)

  • v0.28.5(May 31, 2021)

  • v0.28.4(May 31, 2021)

  • v0.28.3(May 28, 2021)

  • v0.28.2(May 25, 2021)

    • Fix missing Options TypeScript type 3ad1eab
    • Restore main field in package.json (#342) 1338996

    https://github.com/sindresorhus/ky/compare/v0.28.1...v0.28.2

    Source code(tar.gz)
    Source code(zip)
  • v0.28.1(May 14, 2021)

  • v0.28.0(May 4, 2021)

    Breaking

    • Make HTTPError and TimeoutError named exports 6ec7fd7

    Improvements

    • Improve HTTPError messages (#333) ca098f8

    Fixes

    • Fix merging of searchParams in .create() and .extend() (#335) b3c9e88
    • Throw timeouts even if throwHttpErrors option is false (#334) eefcde5

    Meta

    • Move codebase to TypeScript (#330) 2d8d68a

    https://github.com/sindresorhus/ky/compare/v0.27.0...v0.28.0

    Source code(tar.gz)
    Source code(zip)
  • v0.27.0(Feb 20, 2021)

  • v0.26.0(Jan 8, 2021)

    Breaking

    • Ky is now a pure ESM module (#313) d976029
    • Drop UMD build (#315) 1a7ec7f
    • Require Node.js 12 when used in Node.js (#315) 1a7ec7f

    Improvements

    • Add request and options to HTTPError (#309) 519d17e

    Fixes

    • Fix aborting when onDownloadProgress throws an exception (#301) 87c94fd
    • Fix support for ky.stop as return type in BeforeRetryHook TypeScript type (#307) b86ff0a
    • Fix BeforeRetryHook and NormalizedOptions TypeScript type definitions (#308) 5da3da0

    Docs

    • Improve docs for ky.stop (#314) 0ced1f1

    https://github.com/sindresorhus/ky/compare/v0.25.1...v0.26.0

    Source code(tar.gz)
    Source code(zip)
  • v0.25.1(Dec 8, 2020)

  • v0.25.0(Nov 14, 2020)

    Possibly breaking for TypeScript users

    • Fix Deno and React Native compatibility (#295) 2bff89a
      • If you use TypeScript and don't already include the dom library in your tsconfig, you need to do this now.

    Improvements

    • Expose TimeoutError#request (#299) 599a45b

    https://github.com/sindresorhus/ky/compare/v0.24.0...v0.25.0

    Source code(tar.gz)
    Source code(zip)
  • v0.24.0(Sep 28, 2020)

    Breaking

    • Remove response property for the first argument passed to beforeRetry hooks (#276) 9876da1 You can access it on error.response instead. See https://github.com/sindresorhus/ky/pull/276 for details.

    Fixes

    • Fix handling of network errors for beforeRetry hook (#276) 9876da1

    https://github.com/sindresorhus/ky/compare/v0.23.0...v0.24.0

    Source code(tar.gz)
    Source code(zip)
  • v0.23.0(Jul 21, 2020)

  • v0.22.0(Jul 17, 2020)

    • Add support for relative URLs and searchParams even in non-browsers (#271) c9e5206
    • Add parseJson option (#272) 95e7fd7
    • Fix headers construction in old browsers (#270) 71a8d59

    https://github.com/sindresorhus/ky/compare/v0.21.0...v0.22.0

    Source code(tar.gz)
    Source code(zip)
  • v0.21.0(Jul 12, 2020)

  • v0.20.0(May 16, 2020)

  • v0.19.1(Apr 17, 2020)

Owner
Sindre Sorhus
Full-Time Open-Sourcerer. Wants more empathy & kindness in open source. Focuses on Swift & JavaScript. Makes macOS apps, CLI tools, npm packages. Likes unicorns
Sindre Sorhus
SiJago - GraphQL Client for Browser and Node.js

SiJago is GraphQL Client for Browser and Node.js, You can write request GraphQL schema using JavaScript Object Style, Why i create this tools, Because for reducing typo when writing GraphQL schema using HTTP client like Axios, Fetch or GraphQL client using Apollo and also to simplify calling the GraphQL schema easy to understand for human.

Restu Wahyu Saputra 7 Mar 13, 2022
API routes are great for APIs, but for small projects where you have to access server data or hide application logic, you can just call a server function from the client.

API routes are great for APIs, but for small projects where you have to access server data or hide application logic, you can just call a server function from the client.

null 3 Mar 6, 2022
A tiny FPS for js13k

A tiny FPS for js13k

Dominic Szablewski 1.4k Dec 26, 2022
TypeScript with a Borrow Checker. Multi-threaded, Tiny binaries. No GC. Easy to write.

TypeScript with a Borrow Checker. Multi-threaded, Tiny binaries. No GC. Easy to write.

David Alsh 1.4k Dec 19, 2022
A prebuilt Express JS Authentication & Authorization Project based on a REST API interface.

A prebuilt Express JS Authentication & Authorization Project based on a REST API interface. It has the basic authentication and Authorization routes with the latest security measures implemented so that your application is much more secure from day 1. You are welcome to build upon and extend this project as and when required.

Soumalya Bhattacharya 2 Oct 7, 2022
wasteof-client is an npm package for wasteof.money

wasteof-client is an npm package for wasteof.money

Oren Lindsey 2 Jun 16, 2022
Browser fingerprinting library with the highest accuracy and stability.

FingerprintJS is a browser fingerprinting library that queries browser attributes and computes a hashed visitor identifier from them. Unlike cookies a

FingerprintJS 18.1k Dec 31, 2022
a browser detector

Bowser A small, fast and rich-API browser/platform/engine detector for both browser and node. Small. Use plain ES5-version which is ~4.8kB gzipped. Op

Denis Demchenko 5.2k Jan 2, 2023
A Featureful File Browser for Cockpit

Cockpit Navigator A Featureful File System Browser for Cockpit - remotely browse, manage, edit, upload, and download files on your server through your

45Drives 226 Dec 27, 2022
Add weak event listeners from your components/classes based on WeakRefs

Add weak event listeners from your components/classes based on WeakRefs. This package handles the boilerplate for you, which isn't too much anyways but not particularly good looking.

Ashish Shubham 3 Feb 25, 2022
Folder structure-based PHP wiki documentation engine

Mad simple PHP wiki engine for auto-generating documentation webpage with it's own language

Vortex 5 Sep 5, 2022
Max is a Typescript-based Discord bot with many functionalities

Max is a Typescript-based Discord bot with many functionalities. He is both my learning curve for understanding Discord's API as well as my current passion project.

Jack Levreau 4 May 24, 2022
:heavy_dollar_sign: Vibration API Wrappers

This library was to be published hand-to-hand with my article on the Vibration API. You can also view the documentation. Does my Device Support the AP

illyism 144 Nov 23, 2022
🧠 100'den fazla gereksiz bilgi ile oluşturulmuş bir JSON API.

?? Gereksiz Bilgiler API 100'den fazla gereksiz bilgi ile oluşturulmuş bir JSON API.

Orhan Emre Dikicigil 3 Sep 23, 2022
Fullstack nest (API/Monitering/machine learning, etc)

My backend nestjs requirement nodejs v16.13.1 checkpoint [devOnly] test connection to influx for monitoring (remaining security config) [devOnly] stes

NinetyNineNineTeenTales 2 Jan 18, 2022
API for managing authentication, creating Users, Items and Categories for FinancesApp

This is a repository to store all the API for managing authentication, creating users, items and categories. Search for single or multiple records at once, update items and categories and remove both.

Finances App 8 May 10, 2022
Building dynamic form in Angular with Fluent API

ngx-fluent-form Building dynamic form in Angular with Fluent API. Features Support using Fluent API and JSON. Type-safe form configuration.

HyperLifelll9 22 Dec 23, 2022
This web application retrieves real live data from the SpaceX API

This web application retrieves real live data from the SpaceX API. It provides commercial and scientific space travel services, by allowing users to book rockets and join selected space missions.

Sahar Abdel Samad 12 Aug 9, 2022
An arbitrary size Bit-Vector implementation in JavaScript

BitSet.js BitSet.js is an infinite Bit-Array (aka bit vector, bit string, bit set) implementation in JavaScript. That means that if you invert a bit v

Robert Eisele 207 Dec 9, 2022