Skip to content

Commit

Permalink
Allow overriding Content-Type header for JSON requests (#429)
Browse files Browse the repository at this point in the history
  • Loading branch information
sholladay committed Mar 2, 2022
1 parent d74e16d commit d00864f
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 4 deletions.
20 changes: 20 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,26 @@ searchParams.set('drink', 'icetea');
const response = await ky.post(url, {body: searchParams});
```

### Setting a custom `Content-Type`

Ky automatically sets an appropriate [`Content-Type`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) header for each request based on the data in the request body. However, some APIs require custom, non-standard content types, such as `application/x-amz-json-1.1`. Using the `headers` option, you can manually override the content type.

```js
import ky from 'ky';

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

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

### 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 source/core/Ky.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ export class Ky {

if (this._options.json !== undefined) {
this._options.body = JSON.stringify(this._options.json);
this.request.headers.set('content-type', 'application/json');
this.request.headers.set('content-type', this._options.headers.get('content-type') ?? 'application/json');
this.request = new globalThis.Request(this.request, {body: this._options.body});
}
}
Expand Down Expand Up @@ -301,12 +301,12 @@ export class Ky {
}

if (onDownloadProgress) {
transferredBytes += value!.byteLength;
transferredBytes += value.byteLength;
const percent = totalBytes === 0 ? 0 : transferredBytes / totalBytes;
onDownloadProgress({percent, transferredBytes, totalBytes}, value!);
onDownloadProgress({percent, transferredBytes, totalBytes}, value);
}

controller.enqueue(value!);
controller.enqueue(value);
await read();
}

Expand Down
1 change: 1 addition & 0 deletions source/types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ export type InternalOptions = Required<
Omit<Options, 'hooks' | 'retry'>,
'credentials' | 'fetch' | 'prefixUrl' | 'timeout'
> & {
headers: Required<Headers>;
hooks: Required<Hooks>;
retry: Required<RetryOptions>;
prefixUrl: string;
Expand Down
18 changes: 18 additions & 0 deletions test/headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,24 @@ test('sets `content-length` to `0` when requesting PUT with empty body', async t
t.is(headers['content-length'], '0');
});

test('json manual `content-type` header', async t => {
const server = await createHttpTestServer();
server.post('/', echoHeaders);

const headers = await ky
.post(server.url, {
headers: {
'content-type': 'custom',
},
json: {
foo: true,
},
})
.json<IncomingHttpHeaders>();

t.is(headers['content-type'], 'custom');
});

test('form-data manual `content-type` header', async t => {
const server = await createHttpTestServer();
server.post('/', echoHeaders);
Expand Down

0 comments on commit d00864f

Please sign in to comment.