Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for stringifyJson #579

Merged
merged 11 commits into from
May 31, 2024
25 changes: 25 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,31 @@ const json = await ky('https://example.com', {
}).json();
```

##### stringifyJson

Type: `Function`\
Default: `JSON.stringify()`

User-defined JSON-stringifying function.

Use-cases:
1. Stringify JSON with a custom `replacer` function.

```js
import ky from 'ky';
import {DateTime} from 'luxon';

const json = await ky('https://example.com', {
stringifyJson: data => JSON.stringify(data, (key, value) => {
if (key.endsWith('_at')) {
return DateTime.fromISO(value).toSeconds();
}

return value;
})
}).json();
```

##### fetch

Type: `Function`\
Expand Down
2 changes: 1 addition & 1 deletion source/core/Ky.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export class Ky {
}

if (this._options.json !== undefined) {
this._options.body = JSON.stringify(this._options.json);
this._options.body = this._options.stringifyJson?.(this._options.json) ?? JSON.stringify(this._options.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
1 change: 1 addition & 0 deletions source/core/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const stop = Symbol('stop');
export const kyOptionKeys: KyOptionsRegistry = {
json: true,
parseJson: true,
stringifyJson: true,
searchParams: true,
prefixUrl: true,
retry: true,
Expand Down
26 changes: 26 additions & 0 deletions source/types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,32 @@ export type KyOptions = {
*/
parseJson?: (text: string) => unknown;

/**
User-defined JSON-stringifying function.

Use-cases:
1. Stringify JSON with a custom `replacer` function.

@default JSON.stringify()

@example
```
import ky from 'ky';
import {DateTime} from 'luxon';

const json = await ky('https://example.com', {
stringifyJson: data => JSON.stringify(data, (key, value) => {
if (key.endsWith('_at')) {
return DateTime.fromISO(value).toSeconds();
}

return value;
})
}).json();
```
*/
stringifyJson?: (data: unknown) => string;

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

Expand Down
20 changes: 20 additions & 0 deletions test/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -733,3 +733,23 @@ test('parseJson option with promise.json() shortcut', async t => {

await server.close();
});

test('stringifyJson option with request.json()', async t => {
const server = await createHttpTestServer({bodyParser: false});

const json = {hello: 'world'};
const extra = 'extraValue';

server.post('/', async (request, response) => {
const body = await parseRawBody(request);
t.is(body, JSON.stringify({data: json, extra}));
response.end();
});

await ky.post(server.url, {
stringifyJson: data => JSON.stringify({data, extra}),
json,
});

await server.close();
});