new URLSearchParams({ q: ["greeting", "foobar"] }).toString() が返すのは q=greeting%2Cfoobar です。配列が文字列へ暗黙変換され、2つの値が1つのカンマ区切り文字列になりました。これに気づかないまま検索条件をURLへ書き出すと、サーバー側では値が1件として届きます。
URLSearchParams の基本と、同じキーを複数持つクエリの扱い方をまとめます。Next.jsのApp Routerでも、useSearchParams を通じて読み取り専用のインターフェースを利用できます。
基本的なユースケース
URLSearchParams は、クエリ文字列、オブジェクト、キーと値のペアを持つ配列などから生成できます。
const empty = new URLSearchParams();
const fromString = new URLSearchParams("q=greeting");
const fromRecord = new URLSearchParams({ q: "greeting" });
const fromEntries = new URLSearchParams([["q", "greeting"]]);
Map のように、キーと値のペアを反復できるオブジェクトも渡せます。
- 文字列
- 名前を表す文字列と値を表す文字列のペアのリテラル列、もしくはそのような文字列のペアの列を生成するイテレーターを持つ任意のオブジェクト(たとえば FormData のオブジェクト)
- 文字列のキーと文字列の値からなるレコード https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/URLSearchParams#parameters
const map = new Map();
map.set("q", "greeting");
const p = new URLSearchParams(map);
URLSearchParamsを利用する
toString は、保持しているクエリパラメータを文字列として返します。
const p = new URLSearchParams("q=greeting");
console.log(p.toString());
// 'q=greeting'
同じキーを複数指定すると、q=greeting&q=foobar のように出力されます。サーバーが q[]=greeting&q[]=foobar という形式を要求する場合は、キー自体を q[] にします。
const p = new URLSearchParams([
["q", "greeting"],
["q", "foobar"],
]);
p.toString();
// 'q=greeting&q=foobar'
値の追加、削除、置換には append、delete、set を使います。get は指定したキーの最初の値、getAll はすべての値を返します。entries でキーと値を反復でき、sort でキー順に並べ替えることもできます。
同じキーが複数あるオブジェクトを変換する
Next.jsのPages Routerでは、useRouter が返す router.query からクエリを取得できます。同じキーが複数ある場合、値は次のような配列になります。
// router.query
{
q: ["greeting", "foobar"];
}
このオブジェクトをそのまま渡すと、冒頭のカンマ区切り文字列になります。
const p = new URLSearchParams({ q: ["greeting", "foobar"] });
p.toString();
// 'q=greeting%2Cfoobar'
キーと値のペアを持つ配列へ変換すれば、同じキーを複数回追加できます。
const o = {
q: ["greeting", "foobar"],
r: "routing",
};
const entries: [string, string][] = [];
for (const [key, value] of Object.entries(o)) {
if (typeof value === "string") {
entries.push([key, value]);
} else {
value.forEach((val) => {
entries.push([key, val]);
});
}
}
const params = new URLSearchParams(entries);
URL全体がわかる場合は、URL インスタンスの searchParams をそのまま利用できます。
const u = new URL("https://example.com?q=greeting&q=foobar&r=routing#ddd");
u.searchParams.toString();
// 'q=greeting&q=foobar&r=routing'
さいごに
URLを扱う標準APIは、URLSearchParams や URL、URLPattern とすでに充実しています。文字列を手でつなぐ前に使えるものがないか確かめれば、エンコードや複数値をめぐる不具合を減らせます。