Skip to content

Commit

Permalink
Document using FormData (#130)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
szmarczak and sindresorhus committed Apr 24, 2019
1 parent 64c2d29 commit 813d28b
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 6 deletions.
6 changes: 6 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ const Headers = getGlobal('Headers');
const Response = getGlobal('Response');
const fetch = getGlobal('fetch');
const AbortController = getGlobal('AbortController');
const FormData = getGlobal('FormData');

const isObject = value => value !== null && typeof value === 'object';
const supportsAbortController = typeof getGlobal('AbortController') === 'function';
const supportsFormData = typeof FormData === 'function';

const deepMerge = (...sources) => {
let returnValue = {};
Expand Down Expand Up @@ -200,6 +202,10 @@ class Ky {

const headers = new Headers(this._options.headers || {});

if (((supportsFormData && this._options.body instanceof FormData) || this._options.body instanceof URLSearchParams) && headers.has('content-type')) {
throw new Error(`The \`content-type\` header cannot be used with a ${this._options.body.constructor.name} body. It will be set automatically.`);
}

if (json) {
if (this._options.body) {
throw new Error('The `json` option cannot be used with the `body` option');
Expand Down
28 changes: 28 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,34 @@ The error thrown when the request times out.

## Tips

### Sending Form Data

Sending 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`. Setting it manually will result in an error.

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

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`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams).

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

ky.post(url, {
body: searchParams
});
```

### Cancellation

Fetch (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)
Expand Down
8 changes: 4 additions & 4 deletions test/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ test('afterResponse hook accept success response', async t => {
foo: true
};

await t.notThrowsAsync(() => ky.post(
await t.notThrowsAsync(ky.post(
server.url,
{
json,
Expand Down Expand Up @@ -123,7 +123,7 @@ test('afterResponse hook accept fail response', async t => {
foo: true
};

await t.throwsAsync(() => ky.post(
await t.throwsAsync(ky.post(
server.url,
{
json,
Expand Down Expand Up @@ -189,7 +189,7 @@ test('afterResponse hook can throw error to reject the request promise', async t
const expectError = new Error('Error from `afterResponse` hook');

// Sync hook function
await t.throwsAsync(() => ky.get(
await t.throwsAsync(ky.get(
server.url,
{
hooks: {
Expand All @@ -205,7 +205,7 @@ test('afterResponse hook can throw error to reject the request promise', async t
});

// Async hook function
await t.throwsAsync(() => ky.get(
await t.throwsAsync(ky.get(
server.url,
{
hooks: {
Expand Down
9 changes: 9 additions & 0 deletions test/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -352,3 +352,12 @@ test('throws AbortError when aborted by user', async t => {

await t.throwsAsync(response, {name: 'AbortError'});
});

test('throws when using FormData with `content-type` header', t => {
t.throws(() => ky.post('https://example.com', {
body: new URLSearchParams(),
headers: {
'content-type': ''
}
}), 'The `content-type` header cannot be used with a URLSearchParams body. It will be set automatically.');
});
4 changes: 2 additions & 2 deletions test/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ test('common method is normalized', async t => {
response.end();
});

await t.notThrowsAsync(() => ky(server.url, {
await t.notThrowsAsync(ky(server.url, {
method: 'get',
hooks: {
beforeRequest: [
Expand All @@ -28,7 +28,7 @@ test('custom method remains identical', async t => {
response.end();
});

await t.notThrowsAsync(() => ky(server.url, {
await t.notThrowsAsync(ky(server.url, {
method: 'report',
hooks: {
beforeRequest: [
Expand Down

0 comments on commit 813d28b

Please sign in to comment.