Skip to content

Commit d00864f

Browse files
authoredMar 2, 2022
Allow overriding Content-Type header for JSON requests (#429)
1 parent d74e16d commit d00864f

File tree

4 files changed

+43
-4
lines changed

4 files changed

+43
-4
lines changed
 

‎readme.md

+20
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,26 @@ searchParams.set('drink', 'icetea');
525525
const response = await ky.post(url, {body: searchParams});
526526
```
527527

528+
### Setting a custom `Content-Type`
529+
530+
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.
531+
532+
```js
533+
import ky from 'ky';
534+
535+
const json = await ky.post('https://example.com', {
536+
headers: {
537+
'content-type': 'application/json'
538+
}
539+
json: {
540+
foo: true
541+
},
542+
}).json();
543+
544+
console.log(json);
545+
//=> `{data: '🦄'}`
546+
```
547+
528548
### Cancellation
529549

530550
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)

‎source/core/Ky.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ export class Ky {
176176

177177
if (this._options.json !== undefined) {
178178
this._options.body = JSON.stringify(this._options.json);
179-
this.request.headers.set('content-type', 'application/json');
179+
this.request.headers.set('content-type', this._options.headers.get('content-type') ?? 'application/json');
180180
this.request = new globalThis.Request(this.request, {body: this._options.body});
181181
}
182182
}
@@ -301,12 +301,12 @@ export class Ky {
301301
}
302302

303303
if (onDownloadProgress) {
304-
transferredBytes += value!.byteLength;
304+
transferredBytes += value.byteLength;
305305
const percent = totalBytes === 0 ? 0 : transferredBytes / totalBytes;
306-
onDownloadProgress({percent, transferredBytes, totalBytes}, value!);
306+
onDownloadProgress({percent, transferredBytes, totalBytes}, value);
307307
}
308308

309-
controller.enqueue(value!);
309+
controller.enqueue(value);
310310
await read();
311311
}
312312

‎source/types/options.ts

+1
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ export type InternalOptions = Required<
228228
Omit<Options, 'hooks' | 'retry'>,
229229
'credentials' | 'fetch' | 'prefixUrl' | 'timeout'
230230
> & {
231+
headers: Required<Headers>;
231232
hooks: Required<Hooks>;
232233
retry: Required<RetryOptions>;
233234
prefixUrl: string;

‎test/headers.ts

+18
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,24 @@ test('sets `content-length` to `0` when requesting PUT with empty body', async t
149149
t.is(headers['content-length'], '0');
150150
});
151151

152+
test('json manual `content-type` header', async t => {
153+
const server = await createHttpTestServer();
154+
server.post('/', echoHeaders);
155+
156+
const headers = await ky
157+
.post(server.url, {
158+
headers: {
159+
'content-type': 'custom',
160+
},
161+
json: {
162+
foo: true,
163+
},
164+
})
165+
.json<IncomingHttpHeaders>();
166+
167+
t.is(headers['content-type'], 'custom');
168+
});
169+
152170
test('form-data manual `content-type` header', async t => {
153171
const server = await createHttpTestServer();
154172
server.post('/', echoHeaders);

0 commit comments

Comments
 (0)
Please sign in to comment.