Skip to content

Commit ce10fb7

Browse files
committed
Support undefined values for searchParams object
Fixes #293
1 parent 2fb39e9 commit ce10fb7

File tree

4 files changed

+48
-3
lines changed

4 files changed

+48
-3
lines changed

readme.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,13 +161,15 @@ Shortcut for sending JSON. Use this instead of the `body` option. Accepts any pl
161161

162162
##### searchParams
163163

164-
Type: `string | object<string, string | number | boolean> | Array<Array<string | number | boolean>> | URLSearchParams`\
164+
Type: `string | object<string, string | number | boolean | undefined> | Array<Array<string | number | boolean>> | URLSearchParams`\
165165
Default: `''`
166166

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

169169
Accepts any value supported by [`URLSearchParams()`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/URLSearchParams).
170170

171+
When passing an object, `undefined` values are automatically filtered out, while `null` values are preserved and converted to the string `'null'`.
172+
171173
##### prefixUrl
172174

173175
Type: `string | URL`

source/core/Ky.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,18 @@ export class Ky {
130130
return result;
131131
}
132132

133+
// eslint-disable-next-line unicorn/prevent-abbreviations
134+
static #normalizeSearchParams(searchParams: any): any {
135+
// Filter out undefined values from plain objects
136+
if (searchParams && typeof searchParams === 'object' && !Array.isArray(searchParams) && !(searchParams instanceof URLSearchParams)) {
137+
return Object.fromEntries(
138+
Object.entries(searchParams).filter(([, value]) => value !== undefined),
139+
);
140+
}
141+
142+
return searchParams;
143+
}
144+
133145
public request: Request;
134146
protected abortController?: AbortController;
135147
protected _retryCount = 0;
@@ -199,7 +211,7 @@ export class Ky {
199211
// eslint-disable-next-line unicorn/prevent-abbreviations
200212
const textSearchParams = typeof this._options.searchParams === 'string'
201213
? this._options.searchParams.replace(/^\?/, '')
202-
: new URLSearchParams(this._options.searchParams as unknown as SearchParamsInit).toString();
214+
: new URLSearchParams(Ky.#normalizeSearchParams(this._options.searchParams) as unknown as SearchParamsInit).toString();
203215
// eslint-disable-next-line unicorn/prevent-abbreviations
204216
const searchParams = '?' + textSearchParams;
205217
const url = this.request.url.replace(/(?:\?.*?)?(?=#|$)/, searchParams);

source/types/options.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type {RetryOptions} from './retry.js';
66
export type SearchParamsInit = string | string[][] | Record<string, string> | URLSearchParams | undefined;
77

88
// eslint-disable-next-line unicorn/prevent-abbreviations
9-
export type SearchParamsOption = SearchParamsInit | Record<string, string | number | boolean> | Array<Array<string | number | boolean>>;
9+
export type SearchParamsOption = SearchParamsInit | Record<string, string | number | boolean | undefined> | Array<Array<string | number | boolean>>;
1010

1111
export type HttpMethod = 'get' | 'post' | 'put' | 'patch' | 'head' | 'delete';
1212

@@ -88,6 +88,8 @@ export type KyOptions = {
8888
Search parameters to include in the request URL. Setting this will override all existing search parameters in the input URL.
8989
9090
Accepts any value supported by [`URLSearchParams()`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/URLSearchParams).
91+
92+
When passing an object, `undefined` values are automatically filtered out, while `null` values are preserved and converted to the string `'null'`.
9193
*/
9294
searchParams?: SearchParamsOption;
9395

test/main.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,35 @@ test('searchParams option', async t => {
406406
await server.close();
407407
});
408408

409+
test('searchParams option with undefined values', async t => {
410+
const server = await createHttpTestServer();
411+
412+
server.get('/', (request, response) => {
413+
response.end(request.url.slice(1));
414+
});
415+
416+
const objectWithUndefined = {
417+
cats: 'meow',
418+
dogs: undefined,
419+
opossums: 'false',
420+
birds: undefined,
421+
};
422+
423+
const objectWithNull = {
424+
cats: 'meow',
425+
dogs: null as any,
426+
opossums: 'false',
427+
};
428+
429+
// Undefined values should be filtered out
430+
t.is(await ky(server.url, {searchParams: objectWithUndefined}).text(), '?cats=meow&opossums=false');
431+
432+
// Null values should be preserved as string "null"
433+
t.is(await ky(server.url, {searchParams: objectWithNull}).text(), '?cats=meow&dogs=null&opossums=false');
434+
435+
await server.close();
436+
});
437+
409438
test('throwHttpErrors option', async t => {
410439
const server = await createHttpTestServer();
411440
server.get('/', (_request, response) => {

0 commit comments

Comments
 (0)