JavaScript Tips | 文字列ユーティリティ:URL 系 - クエリ取得

JavaScript JavaScript
スポンサーリンク

「クエリ取得」で何をしたいのかをはっきりさせる

ここでの「クエリ取得」は、URL の ? 以降(クエリ文字列)から、パラメータを取り出して使いやすい形にすることです。

/search?q=JavaScript&page=2
この qpage の値を、JavaScript から安全に取り出したい。

画面遷移後に「さっきの検索条件」を復元したり、
URL で渡された tokenid を使って処理したりするために、
「クエリをちゃんとパースしてオブジェクトにする」ユーティリティがあると、実務でかなり効きます。


まずは「クエリ文字列」の形を整理する

典型的な形

クエリ文字列は、基本的にこういう形をしています。

?q=JavaScript&page=2&sort=created_at

ルールを分解するとこうです。

  • ? のあとに「クエリ部分」が続く
  • & で「パラメータ同士」を区切る
  • 各パラメータは key=value の形
  • key と value は URL エンコードされている(%xx 形式が混ざる)

なので、「クエリ取得ユーティリティ」がやるべきことは、ざっくり言うとこうです。

  1. 先頭の ? を外す
  2. & で分割して key=value のペアにする
  3. keyvaluedecodeURIComponent で元に戻す
  4. オブジェクトに詰める

これを毎回手書きするのは面倒なので、関数に閉じ込めます。


基本形:クエリ文字列 → オブジェクト

parseQueryString の実装

まずは、"?q=JavaScript&page=2" のような文字列を受け取って、
{ q: "JavaScript", page: "2" } のようなオブジェクトにする関数です。

function parseQueryString(qs) {
  const result = {};

  if (!qs) return result;

  const query = qs.startsWith("?") ? qs.slice(1) : qs;

  if (query === "") return result;

  const pairs = query.split("&");

  for (const pair of pairs) {
    if (!pair) continue;

    const [rawKey, rawValue = ""] = pair.split("=", 2);

    const key = decodeURIComponent(rawKey);
    const value = decodeURIComponent(rawValue);

    result[key] = value;
  }

  return result;
}
JavaScript

重要ポイントをかみ砕いて説明する

ここはしっかり理解しておきたいところです。

1. ? を外してから処理する

const query = qs.startsWith("?") ? qs.slice(1) : qs;
JavaScript

location.search"?q=..." のように ? 付きで返ってきます。
一方で、関数に直接 "q=..." を渡したいこともあります。

どちらでも動くように、「先頭が ? なら外す」という処理を入れています。

2. & で分割して「ペア」にする

const pairs = query.split("&");
JavaScript

ここで ["q=JavaScript", "page=2"] のような配列になります。
空文字(&& が続いたときなど)はスキップしています。

3. key=value を 2 つに分けるときの注意

const [rawKey, rawValue = ""] = pair.split("=", 2);
JavaScript

split("=", 2) としているのは、value= が含まれていても壊れないようにするためです。

例えば、"sort=created_at desc" は問題ないですが、
"token=a=b=c" のような値も理論上ありえます。

split("=", 2) にしておけば、

  • rawKey"token"
  • rawValue"a=b=c"

と、最初の = だけで分割してくれます。

4. key も value も decodeURIComponent する

const key = decodeURIComponent(rawKey);
const value = decodeURIComponent(rawValue);
JavaScript

エンコード側で encodeURIComponent を使っている前提なので、
デコード側も decodeURIComponent でペアにしてあげます。

スペースや日本語、&= などが元の文字に戻ります。


実際の使用例でイメージを固める

location.search からクエリを取得する

ブラウザなら、こう書けます。

const params = parseQueryString(window.location.search);

// 例: URL が /search?q=JavaScript%20%E5%85%A5%E9%96%80&page=2 のとき
// params は { q: "JavaScript 入門", page: "2" }
JavaScript

あとは、フォームに値を戻したり、処理に使ったりできます。

searchInput.value = params.q ?? "";
currentPage = Number(params.page ?? 1);
JavaScript

特定のキーだけを取り出す小さなユーティリティ

よく使うのは「特定の 1 つのパラメータだけ欲しい」パターンです。

function getQueryParam(qs, name) {
  const params = parseQueryString(qs);
  return params[name];
}
JavaScript

使い方はこうです。

const q = getQueryParam(window.location.search, "q");
const token = getQueryParam(window.location.search, "token");
JavaScript

「全部のパラメータはいらない、これだけ欲しい」というときに、
parseQueryString の上にこういう薄いラッパーを乗せておくと、コードが読みやすくなります。


配列・複数値をどう扱うか(少し応用)

tag=js&tag=ts を配列として扱いたい場合

クエリ生成側で「同じキーを複数回出す」設計にしていると、
取得側でもそれを配列として扱いたくなります。

?tag=js&tag=ts&q=frontend

これを

{ tag: ["js", "ts"], q: "frontend" }
JavaScript

のようにしたい場合のバージョンです。

function parseQueryStringWithArray(qs) {
  const result = {};

  if (!qs) return result;

  const query = qs.startsWith("?") ? qs.slice(1) : qs;
  if (query === "") return result;

  const pairs = query.split("&");

  for (const pair of pairs) {
    if (!pair) continue;

    const [rawKey, rawValue = ""] = pair.split("=", 2);

    const key = decodeURIComponent(rawKey);
    const value = decodeURIComponent(rawValue);

    if (key in result) {
      const current = result[key];
      if (Array.isArray(current)) {
        current.push(value);
      } else {
        result[key] = [current, value];
      }
    } else {
      result[key] = value;
    }
  }

  return result;
}
JavaScript

動きはこうなります。

parseQueryStringWithArray("?tag=js&tag=ts&q=frontend");
// { tag: ["js", "ts"], q: "frontend" }
JavaScript

「同じキーが複数回出てきたら配列にする」というルールを決めておくと、
タグ検索や複数選択の条件を扱いやすくなります。


設計として意識してほしいこと

「クエリ生成」と「クエリ取得」をペアで考える

前に話した「クエリ生成」と今回の「クエリ取得」は、セットで設計すると強いです。

生成側:

toQueryString({ q: "JavaScript 入門", tag: ["js", "ts"] });
// "?q=JavaScript%20%E5%85%A5%E9%96%80&tag=js&tag=ts"
JavaScript

取得側:

parseQueryStringWithArray("?q=JavaScript%20%E5%85%A5%E9%96%80&tag=js&tag=ts");
// { q: "JavaScript 入門", tag: ["js", "ts"] }
JavaScript

このように、「どう出すか」と「どう読むか」をペアで決めておくと、
バックエンドとの連携や画面間の遷移がとても安定します。

「どこで decode するか」をはっきりさせる

なんとなく decodeURIComponent をあちこちで呼び始めると、
二重デコードやエラーの原因になります。

  • 「URL からクエリを読むときだけ decode する」
  • 「それ以外の文字列には decode をかけない」

という線引きを、ユーティリティのレイヤーで決めておくと、
アプリ全体の挙動が読みやすくなります。


ちょっとだけ手を動かしてみる

コンソールで、次の順番で試してみてください。

parseQueryString("?q=JavaScript%20%E5%85%A5%E9%96%80&page=2");
parseQueryString("tag=A%26B");

parseQueryStringWithArray("?tag=js&tag=ts&q=frontend");

getQueryParam("?q=hello&x=1", "q");
JavaScript

「どのキーがどうデコードされて、どんなオブジェクトになるか」を、
自分の目で確認してみてください。

そのうえで、自分のプロジェクトに

export function parseQueryString(...) { ... }
export function parseQueryStringWithArray(...) { ... }
export function getQueryParam(...) { ... }
JavaScript

を置いて、

  • クエリ全体が欲しい → parseQueryString / parseQueryStringWithArray
  • 1 個だけ欲しい → getQueryParam

という使い分けをルール化してみてください。

それだけで、あなたの「クエリ取得」は、
場当たり的な location.search.split("&") から、
意図と安全性を両立した“業務レベルの URL クエリユーティリティ”に一段レベルアップします。

タイトルとURLをコピーしました