Menu
日本語

JavaScriptでURLとクエリ文字列を扱う(URLSearchParams)

正規表現に頼らずに、URLオブジェクトとURLSearchParamsでURLをパース・組み立て・編集する方法を、エッジケースも含めて解説します。

URLを文字列操作でパースするのはもうやめよう

URL APIが登場する前は、みんなsplit('?')や正規表現、そして祈りを駆使してURLを切り刻んでいました。値に&=、スペース、非ASCII文字が混ざらない限りは、それでもなんとか動いていたんです。でも一度そういう文字が入れば、途端に壊れる。ブラウザにもNode.jsにも、ちゃんとしたパーサーが最初から用意されています。素直にそれを使いましょう。

index.js
Output
Click Run to see the output here.

呼び出し一発で、URL の各パーツがすでにバラされて、しかもデコードまで済んだ状態で手に入ります。不正な URL を渡すとコンストラクタは TypeError を投げますが、これはむしろ望ましい挙動です。おかしな URL は黙って通すよりも、その場で派手に落ちてくれたほうが、後続の処理でゴミを垂れ流すより安全ですよね。

クエリパラメータを取得する

URL オブジェクトには必ず .searchParams プロパティがあり、これが URLSearchParams オブジェクトです。クエリ文字列の読み書きはすべてこいつに任せられます。

index.js
Output
Click Run to see the output here.

押さえておきたいポイントがいくつかあります。

  • 値はデコード済みで返ってきます。?name=Ada%20Lovelace なら "Ada Lovelace" が取得できます。
  • すべて文字列として扱われます。"2"2 ではありません。数値として使いたい場合は Number() で変換しましょう。
  • 同じキーが複数あっても問題ありません。get は最初にマッチしたものだけを返し、getAll はすべての値を配列で返します。
  • 存在しないキーは undefined ではなく null を返します。なので ?? "default" との組み合わせが便利です。

クエリ文字列を組み立てる

URLSearchParams を使えば、クエリ文字列をゼロから組み立てられます。エスケープを手動でやる必要も、& で自分でつなぐ必要もありません。

index.js
Output
Click Run to see the output here.

オブジェクトから生成することもできます。[キー, 値] のペアを返すイテラブルならなんでも使えますし、普通のオブジェクトでもOKです。

index.js
Output
Click Run to see the output here.

setappend の違い: set は既存の値を上書きします。append は別の値を追加します。同じキーが複数回登場し得る場合(タグやフィルタなど)は append、単一の値しか持たないパラメータには set を使い分けましょう。

URL を書き換える

URL はライブオブジェクトなので、searchParams をいじれば .search.href も自動的に更新されます。

index.js
Output
Click Run to see the output here.

既存のURLにクエリパラメータを追加するなら、これが一番スマートな書き方です。「URLにすでに?が付いてるかな?」とチェックしたり、区切り文字として&?のどちらを使うか悩む必要もありません。

URLの他の部分も、同じ要領で書き換えられます。

index.js
Output
Click Run to see the output here.

パラメータをループで取り出す

URLSearchParams はイテラブルなので、for...of で回すと [キー, 値] のペアが順番に取れます。配列などと同じように keys()values()entries() といったヘルパーも用意されています。

index.js
Output
Click Run to see the output here.

キーの重複はそのまま保持される点に注目してください。tag = web の次に tag = beginner が別エントリとして出てきます。これはクエリ文字列の実際の中身に忠実な挙動です。

デバッグ用にサクッとプレーンなオブジェクトで中身を確認したいときは Object.fromEntries が便利です。ただし重複キーは潰れてしまい、最後の値だけが残る点には注意してください。

index.js
Output
Click Run to see the output here.

デバッグ目的なら問題ありませんが、同じキーが複数回登場しうる場合は正しく動作しません。

相対URLにはベースが必要

new URL("/search?q=js") を単独で呼ぶとエラーになります。相対パスだけでは有効なURLにならないからです。第2引数にベースURLを渡しましょう。

index.js
Output
Click Run to see the output here.

この解決ルールは、ブラウザが <a href> を解釈するときとまったく同じです。先頭が / ならホストからの絶対パス、スラッシュなしなら現在のパスからの相対、.. は 1 階層上に上がります。設定値のベース URL から API の URL を組み立てるときにかなり重宝します。

ブラウザ上では、window.location.href がそのまま現在ページの URL を解析するためのベースとして使えます。

const u = new URL(window.location.href);
const page = u.searchParams.get("page") ?? "1";

不正な URL を扱う

URL コンストラクタは、フォーマットが崩れた入力を渡すと例外を投げます。これ自体はありがたい仕様なのですが、ユーザーが入力した文字列や外部システムから受け取った値をパースするときは、try/catch で囲む必要があります。

index.js
Output
Click Run to see the output here.

モダンな実行環境では URL.canParse(input) も使えます。これは真偽値を返すチェック用のメソッドで、URL が有効かどうか確かめたいだけなら try/catch でわざわざ囲む必要がありません。

index.js
Output
Click Run to see the output here.

ちょっとした実用サンプル

ここまでの内容をまとめて、URL から現在のフィルタを読み取り、値を書き換えて、遷移先となる新しい URL を組み立ててみましょう。

index.js
Output
Click Run to see the output here.

null を渡すとそのパラメータが削除されます。それ以外の値を渡すと、設定もしくは上書きになります。フィルター UI やページネーション、ディープリンクを作るときには、形は違えど結局このパターンを書くことになります。

まとめ

  • new URL(string) は URL を意味のあるパーツに分解してくれます。不正な文字列を渡すと例外になります。
  • url.searchParamsURLSearchParams なので、getgetAllsetappenddeletehas をそのまま使えます。
  • エンコードは自動で処理されます。自分で文字列を組み立てているとき以外、encodeURIComponent の出番はありません。
  • 相対パスを解決したいときは、第2引数にベース URL を渡しましょう。
  • 信頼できない入力のバリデーションには URL.canParse(または try/catch)が便利です。

.split('?') で URL を分割したくなったときや、正規表現でクエリパラメータを抜き出したくなったときは、代わりにこれらの API を使ってください。コードは短く、挙動は正確で、しかもランタイムに最初から入っています。

よくある質問

JavaScriptでURLをパースするには?

文字列をURLコンストラクタに渡すだけです。const u = new URL('https://example.com/path?x=1')のように書くと、protocolhostpathnamesearchhash、そしてsearchParamsといったプロパティにアクセスできます。不正なURLを渡すと例外を投げるので、外部から受け取った文字列をパースする場合はtry/catchで囲んでおくと安心です。

クエリ文字列のパラメータを取得するには?

url.searchParams.get('name')を使います。デコード済みの値が返ってきて、該当するパラメータが無ければnullになります。?tag=a&tag=bのように同じキーが複数回出てくるケースでは、searchParams.getAll('tag')で配列としてまとめて取得できます。

URLとURLSearchParamsの違いは?

URLはプロトコル、ホスト、パス、クエリ、ハッシュといったURL全体を扱うオブジェクトです。一方、URLSearchParamsはクエリ文字列の部分だけを担当し、a=1&b=2のような文字列を単独で組み立てたり解析したりできます。URLインスタンスには.searchParamsプロパティが用意されていて、これがそのURLに紐づくURLSearchParamsとして機能します。

クエリパラメータを自分でエンコードする必要はある?

基本的には不要です。URLSearchParamssetappendを呼んだり、文字列として取り出したりするときに、キーと値は自動でエンコードされます。スペース、&=、Unicode文字もきちんと処理してくれます。encodeURIComponentを自分で呼ぶのは、どうしても文字列を手組みする場合だけで、普段はそもそも手組みしないのが正解です。

Coddyでコードを学ぼう

始める