まず「クエリ生成」で何を自動化したいのか
ここで言う「クエリ生成」は、オブジェクト(連想配列)から ?key=value&key2=value2 みたいなクエリ文字列を、安全に・楽に作るユーティリティのことです。
検索条件やページ番号、ソート条件などを URL に付けたいとき、毎回手で
"/search?q=" + encodeURIComponent(q) + "&page=" + page
JavaScriptと書いていると、すぐにぐちゃぐちゃになります。
だから、
toQueryString({ q: "JavaScript 入門", page: 2, sort: "created_at desc" });
// "?q=JavaScript%20%E5%85%A5%E9%96%80&page=2&sort=created_at%20desc"
JavaScriptのように、「オブジェクト → クエリ文字列」を一発でやってくれる関数を持っておくと、実務でめちゃくちゃ効きます。
クエリ文字列の基本ルールをざっくり整理する
形はシンプル、でも落とし穴が多い
クエリ文字列の基本形はこうです。
?key1=value1&key2=value2&key3=value3
ここで大事なのは 2 つです。
?は最初の 1 回だけ&で「パラメータ同士」をつなぐ
そして、key と value は URL エンコードしておく必要がある、というのが一番のポイントです。
q = "JavaScript 入門"
→ "q=" + encodeURIComponent("JavaScript 入門")
→ "q=JavaScript%20%E5%85%A5%E9%96%80"
JavaScriptエンコードしないと、スペースや &、= が混ざったときに壊れます。
実務で使える「クエリ生成」ユーティリティの基本形
オブジェクト → ?key=value&... を作る
まずは、シンプルな版から作ります。
function toQueryString(params) {
if (!params || typeof params !== "object") {
return "";
}
const parts = [];
for (const [key, value] of Object.entries(params)) {
if (value == null) {
// null / undefined はクエリに含めない
continue;
}
const encodedKey = encodeURIComponent(key);
const encodedValue = encodeURIComponent(String(value));
parts.push(`${encodedKey}=${encodedValue}`);
}
if (parts.length === 0) {
return "";
}
return "?" + parts.join("&");
}
JavaScript重要ポイントをかみ砕いて説明する
ここはしっかり理解してほしいところです。
1. params は「オブジェクト前提」にする
if (!params || typeof params !== "object") {
return "";
}
JavaScriptクエリ生成は「キーと値のセット」が前提なので、
文字列や数値が来たら何もせず空文字を返す、という割り切りにしています。
2. null / undefined はクエリに含めない
if (value == null) continue;
JavaScript== null は null と undefined の両方を拾えます。
?page=&q= のような「空のパラメータ」を付けると、
サーバー側の解釈がややこしくなることが多いので、
「値がないものはそもそも付けない」という方針にしておくとシンプルです。
3. key も value も必ず encodeURIComponent
const encodedKey = encodeURIComponent(key);
const encodedValue = encodeURIComponent(String(value));
JavaScriptここが一番大事です。
- key にも
-や.以外の記号が入ることがある - value にはスペース、日本語、
&、=など何でも入る
ので、両方とも encodeURIComponent しておくのが安全です。
4. 先頭の ? をユーティリティ側で付ける
return "?" + parts.join("&");
JavaScript呼び出し側で毎回 ? を付けるようにすると、"/search" + "?" + toQueryString(...) みたいに二重になったり、? を付け忘れたりしがちです。
「クエリ文字列を作る関数は、? から始まる文字列を返す」と決めておくと、
使う側のコードがスッキリします。
実際の使用例でイメージを固める
検索画面の URL を組み立てる
const params = {
q: "JavaScript 入門",
page: 2,
sort: "created_at desc",
};
const url = "/search" + toQueryString(params);
// "/search?q=JavaScript%20%E5%85%A5%E9%96%80&page=2&sort=created_at%20desc"
JavaScriptここで、スペースや日本語がちゃんと %xx 形式に変わっていることがポイントです。
条件によってパラメータを付けたり消したり
const params = {
q: keyword || undefined,
page: currentPage > 1 ? currentPage : undefined,
sort: sortKey || undefined,
};
const url = "/search" + toQueryString(params);
JavaScriptundefined にしておけば、toQueryString 側でスキップされるので、
「指定されていない条件は URL に出さない」という挙動を簡単に実現できます。
配列や複数値をどう扱うか(少し応用)
tags=js&tags=ts のように同じキーを複数回出したい場合
タグ検索などで、同じキーを複数回出したいことがあります。
?tag=js&tag=ts&tag=node
これをサポートしたい場合は、配列を特別扱いするように拡張できます。
function toQueryStringWithArray(params) {
if (!params || typeof params !== "object") {
return "";
}
const parts = [];
for (const [key, value] of Object.entries(params)) {
if (value == null) continue;
const encodedKey = encodeURIComponent(key);
if (Array.isArray(value)) {
for (const v of value) {
if (v == null) continue;
const encodedValue = encodeURIComponent(String(v));
parts.push(`${encodedKey}=${encodedValue}`);
}
} else {
const encodedValue = encodeURIComponent(String(value));
parts.push(`${encodedKey}=${encodedValue}`);
}
}
if (parts.length === 0) {
return "";
}
return "?" + parts.join("&");
}
JavaScript使い方はこうです。
toQueryStringWithArray({
q: "js",
tag: ["frontend", "node"],
});
// "?q=js&tag=frontend&tag=node"
JavaScript「配列は同じキーを複数回出す」というルールを決めておくと、
サーバー側でも扱いやすくなります。
設計として意識してほしいこと
「エンコードの責務」をユーティリティに閉じ込める
クエリ生成で一番やりがちなのは、
画面ごとにバラバラに
"?q=" + encodeURIComponent(q) + "&page=" + page
JavaScriptと書き始めてしまうことです。
そうすると、
- ある画面では
pageをエンコードしていない - 別の画面では
sortを付け忘れている - どこかで
?が二重になっている
といった「地味だけど面倒なバグ」が増えます。
だからこそ、
export function toQueryString(params) { ... }
export function toQueryStringWithArray(params) { ... }
JavaScriptのようなユーティリティを 1 箇所に置いて、
「クエリ文字列を作るときは必ずここを通す」と決めてしまうのが、業務レベルの設計です。
「クエリ生成」と「URL 全体の組み立て」を分ける
URL 全体はこう分けて考えるとスッキリします。
- パス部分:
"/search"など、自分で書く - クエリ部分:
toQueryString(params)に任せる
const baseUrl = "/search";
const qs = toQueryString(params);
const url = baseUrl + qs;
JavaScriptこうしておくと、
- ベース URL を変えたいときは
baseUrlだけ変えればいい - クエリの中身を変えたいときは
paramsだけ変えればいい
という、きれいな責務分担になります。
ちょっとだけ手を動かしてみる
コンソールで、次のあたりを試してみてください。
toQueryString({ q: "JavaScript 入門", page: 2 });
toQueryString({ q: "A&B", sort: "created_at desc" });
toQueryStringWithArray({ tag: ["js", "ts"], q: "frontend" });
JavaScript出てきた文字列を眺めながら、
- スペースや
&がどう%xxに変わっているか - 配列がどう複数のパラメータに展開されているか
を確認してみてください。
そのうえで、自分のプロジェクトに
export function toQueryString(...) { ... }
export function toQueryStringWithArray(...) { ... }
JavaScriptを置いて、
「クエリを作るときは必ずこの関数を通す」
というルールにしてしまえば、
あなたの URL 周りのコードは、
場当たり的な文字列連結から、意図と安全性を両立した“業務レベルのクエリ生成ユーティリティ”に一段レベルアップします。
