【Node.js】URLとURLSearchParamsの使い方|パース・クエリ取得・組み立て

【Node.js】URLとURLSearchParamsの使い方|パース・クエリ取得・組み立て Node.js

URLからホスト名やパス、クエリ(?key=value)を取り出したい、あるいはクエリ付きのURLを組み立てたい——Node.jsでは標準のURLURLSearchParamsでこれらを安全に扱えます。どちらもブラウザと共通のAPIで、追加のライブラリは不要です。古いurl.parse()は非推奨になっており、現在はnew URL()を使うのが正しい方法です。

つまずきやすいのは、相対URL(/pathだけ)は基準となるbaseを渡さないとエラーになること、そして存在しないクエリを取得するとnullが返ることです。また、URLSearchParams日本語や記号を自動でURLエンコードしてくれます。この記事では、実機のNode.jsで確認しながら、URLの扱いを整理します。

先に結論

  • URLの分解はconst u = new URL("https://...")u.hostnameu.pathnameなどで取り出せます。
  • クエリはu.searchParams.get("key")で取得(無いとnull)。
  • 相対URLには第2引数のbaseが必要です(new URL("/path", "https://example.com"))。
  • クエリの組み立てはnew URLSearchParams({...})日本語・記号は自動エンコードされます。
  • 同名キーが複数あるときはgetAll("key")で配列として取得します。
  • fetchのURL組み立てにそのまま使えます。

組み立てたURLで通信するならfetchでAPIを叩く、サーバー側でクエリを受け取るならExpressのreq.query、低レベルな処理はhttpモジュールもあわせて参考になります。

スポンサーリンク

URLを分解する new URL

new URL()に完全なURL文字列を渡すと、各部分にプロパティでアクセスできるオブジェクトになります。プロトコル・ホスト名・ポート・パス・クエリ・ハッシュなどを簡単に取り出せます。

URLを分解する
const u = new URL("https://example.com:8080/path/page?name=tanaka&age=30#section");

console.log(u.protocol);   // https:
console.log(u.hostname);   // example.com
console.log(u.port);       // 8080
console.log(u.pathname);   // /path/page
console.log(u.search);     // ?name=tanaka&age=30
console.log(u.hash);       // #section

実機でも、protocol=https:hostname=example.comport=8080pathname=/path/pagehash=#sectionと、URLの各部分が正しく取り出せました。protocolには末尾にコロン(:)が、hashには先頭にシャープ(#)が付く点に注意してください。手で文字列を分割するより、はるかに正確で安全です。

クエリを取得する searchParams

クエリ(?key=value)はu.searchParamsから取得します。get("キー")で値を取り出せます。存在しないキーを指定するとnullが返る点に注意します。

クエリの取得
const u = new URL("https://example.com/search?name=tanaka&age=30");

console.log(u.searchParams.get("name"));   // tanaka
console.log(u.searchParams.get("age"));    // 30(ただし文字列)
console.log(u.searchParams.get("xxx"));    // null(存在しないキー)
console.log(u.searchParams.has("name"));   // true(あるかどうか)

実機でも、get("name")tanaka、存在しないget("xxx")ではnullが返りました。取得できる値はすべて文字列なので、数値として使うときはNumber()で変換します。キーがあるかどうかだけ知りたいときはhas()が便利です。undefinedではなくnullが返る点は、判定でif (value === null)のように扱うときに意識しておくとよいです。

相対URLはbaseが必要

ここが注意点です。new URL()/pathのような相対URL(ホスト名が無いもの)だけを渡すとエラーになります。基準となるURL(base)を第2引数に渡すと、それと組み合わせて解決してくれます。

相対URLとbase
// NG: 相対URLだけだとエラー
// new URL("/api/users");
// → TypeError: Invalid URL

// OK: 第2引数に base を渡す
const u = new URL("/api/users", "https://example.com");
console.log(u.href);   // https://example.com/api/users

// 相対パスの解決にも使える
const u2 = new URL("../images/logo.png", "https://example.com/page/index.html");
console.log(u2.href);  // https://example.com/images/logo.png
相対URLだけではInvalid URLエラー

実機で確認したところ、new URL("/path")のようにホスト名の無い相対URLだけを渡すとTypeError: Invalid URLになりました。一方、new URL("/api/users", "https://example.com")のように第2引数にbase(基準URL)を渡すとhttps://example.com/api/usersと正しく解決されました。リンク先のURLやAPIのパスを組み立てるとき、ベースのドメインが分かっているなら第2引数に渡すのが定番です。../のような相対パスも、baseを基準に正しく解決してくれるため、自分でパスを連結するより安全です。フルURL(https://から始まるもの)の場合は、第2引数は不要です。

クエリを組み立てる URLSearchParams

逆に、クエリ文字列を組み立てたいときはURLSearchParamsを使います。オブジェクトを渡すと、key=value&key=valueの形式に変換してくれます。しかも日本語や記号は自動でURLエンコードされます。

クエリを組み立てる(自動エンコード)
const params = new URLSearchParams({
  q: "猫 かわいい",   // 日本語やスペースを含む
  page: "2",
});

console.log(params.toString());
// q=%E7%8C%AB+%E3%81%8B%E3%82%8F%E3%81%84%E3%81%84&page=2
//  → 日本語もスペースも自動でエンコードされる
エンコードを自分でやらなくてよい

実機で確認したところ、new URLSearchParams({ q: "猫 かわいい", page: "2" })toString()するとq=%E7%8C%AB+%E3%81%8B%E3%82%8F%E3%81%84%E3%81%84&page=2となり、日本語はパーセントエンコード、スペースは+に自動変換されました。URLに日本語やスペース、&などの記号をそのまま入れると壊れてしまいますが、URLSearchParamsを使えばエンコードを自分で書く必要がありませんencodeURIComponentを手で呼んで連結するより安全で確実です。検索キーワードやフィルタ条件をクエリにするときは、必ずURLSearchParamsを通すようにしましょう。

同名キー・追加・変更

tag=a&tag=bのように同じキーが複数ある場合はgetAll()で配列として取得します。append()で追加、set()で上書き、delete()で削除もできます。

同名キーと編集
const params = new URLSearchParams("tag=a&tag=b&tag=c");

console.log(params.get("tag"));      // a(getは最初の1つだけ)
console.log(params.getAll("tag"));   // ["a", "b", "c"](全部)

// 追加・上書き・削除
params.append("page", "2");   // 追加
params.set("tag", "x");       // tag をすべて x に置き換え
params.delete("page");        // 削除
console.log(params.toString());

実機でも、tag=a&tag=b&tag=cに対してget("tag")は最初のaだけ、getAll("tag")["a","b","c"]と全部を返しました。同じキーが複数あるかもしれないクエリでは、getだと最初の1つしか取れないので、getAllを使います。appendは同名キーを増やし、setは同名キーをまとめて1つに置き換える、という違いも覚えておくと便利です。

fetchと組み合わせる(実用)

実際のよくある使い方が、fetchでAPIを叩くときのURL組み立てです。new URL()でベースを作り、searchParams.set()でクエリを足していくと、安全にURLを構築できます。

API URLを組み立ててfetch
// ベースURLを作り、クエリを足していく
const url = new URL("https://api.example.com/search");
url.searchParams.set("keyword", "node.js");
url.searchParams.set("limit", "10");

console.log(url.href);
// https://api.example.com/search?keyword=node.js&limit=10

// そのまま fetch に渡せる
const res = await fetch(url);   // URLオブジェクトを直接渡せる
const data = await res.json();

実機でも、new URL("https://api.example.com/search")searchParams.set("keyword", "node.js")を足すと、https://api.example.com/search?keyword=node.jsという正しいURLが組み立てられました。fetchにはURLオブジェクトをそのまま渡せるため、url.hrefに変換する必要もありません。クエリを文字列連結で作ると、エンコード漏れや&の付け忘れでバグになりがちですが、この方法なら安全です。

主なプロパティ・メソッド

よく使うものをまとめます。

書き方 働き
new URL(str[, base]) URLを分解(相対なら base 必須)
u.hostname / u.pathname ホスト名 / パス
u.searchParams.get(key) クエリの値(無いと null)
u.searchParams.getAll(key) 同名キーを配列で取得
new URLSearchParams(obj) クエリを組み立て(自動エンコード)
params.set / append / delete クエリの編集
params.toString() クエリ文字列にする

よくある失敗

相対URLをbaseなしで渡す

Invalid URLエラーになります。第2引数にbaseを渡します。

存在しないクエリの戻り値をそのまま使う

無いキーはnullです。nullチェックをしてから使います。

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

エンコード漏れの原因です。URLSearchParamsで自動エンコードします。

同名キーをgetで取ろうとする

getは最初の1つだけです。複数あるならgetAllを使います。

古いurl.parse()を使う

非推奨です。new URL()を使います。

よくある質問

QURLからホスト名やクエリを取り出すには?
Aconst u = new URL("https://...")で分解し、u.hostnameでホスト名、u.pathnameでパス、u.searchParams.get("key")でクエリの値を取得します。手で文字列を分割するより正確で安全です。
Q「Invalid URL」エラーが出ます。
A/pathのような相対URLをnew URL()にそのまま渡すとこのエラーになります。第2引数に基準となるURL(base)を渡してください。new URL("/api", "https://example.com")のようにすると正しく解決されます。
Qクエリ文字列を安全に組み立てるには?
Anew URLSearchParams({ key: value })を使います。toString()key=value&...の形式になり、日本語やスペース、記号は自動でURLエンコードされます。文字列連結で作るとエンコード漏れの原因になるため、この方法が安全です。
Q同じ名前のクエリが複数あるときは?
AsearchParams.get("key")は最初の1つしか返しません。tag=a&tag=bのように同名キーが複数ある場合は、searchParams.getAll("key")を使うと["a", "b"]のように配列で全部取得できます。
Qurl.parse()との違いは?
Aurl.parse()は古いAPIで非推奨です。現在はnew URL()を使うのが推奨されています。new URL()はブラウザと共通のAPIで、searchParamsによる安全なクエリ操作など機能も豊富です。新しいコードではnew URL()を使ってください。

まとめ

  • URLの分解はnew URL(str)hostnamepathnamesearchParamsで取り出します。
  • クエリ取得はsearchParams.get無いとnull)、複数はgetAll
  • 相対URLは第2引数のbaseが必要です。
  • 組み立てはURLSearchParams日本語・記号は自動エンコードされます。
  • fetchにはURLオブジェクトをそのまま渡せます。

URLの解析と組み立ては、URLURLSearchParamsに任せるのが安全で確実です。「相対URLはbase」「クエリ組み立てはURLSearchParams」の2点を押さえれば、エンコード漏れやパースミスといった定番のバグを避けられます。fetchと組み合わせて、APIとの連携をすっきり書いてみてください。