JavaScript で URL パラメータを取得するには、new URLSearchParams(location.search).get('キー名') と書きます。 URLSearchParams は、URL の ? 以降(クエリ文字列)を読み書きするための標準クラスで、ブラウザでも Node.js でも追加のライブラリなしで使えます。

この記事では、URLSearchParams の 全メソッドを引数・戻り値・実行結果つきの早見表 にまとめ、取得・オブジェクト化・追加・削除・URL の書き換え・fetch への付与を目的別のコピペ用コードで示します。さらに、「+」と %20、同名キー、空の値、日本語、a[]= 形式、ハッシュ内のパラメータといった つまずきやすい挙動を、Node.js 22.22.2 と Chromium 141 の実行結果で比較 しました。

URLSearchParams の全メソッド早見表

const p = new URLSearchParams('?q=js&page=2&tag=a&tag=b'); に対して、上から順に実行した結果です(Node.js 22.22.2)。

メソッド 引数 戻り値 実行例 結果
get(name) キー名 最初の値(文字列)。なければ null p.get('q') 'js'
p.get('none') null
p.get('tag') 'a'(最初の1つ)
getAll(name) キー名 すべての値の配列。なければ空配列 p.getAll('tag') ['a', 'b']
p.getAll('none') []
has(name) キー名 真偽値 p.has('q') true
has(name, value) キー名と値 その組があるか p.has('tag', 'b') true
p.has('tag', 'z') false
size (プロパティ) 組の数 p.size 4
set(name, value) キー名と値 undefined(p 自体が変わる) p.set('page', '3') q=js&page=3&tag=a&tag=b
p.set('tag', 'x') q=js&page=3&tag=x(同名は1つに)
append(name, value) キー名と値 undefined p.append('tag', 'y') q=js&page=3&tag=x&tag=y
delete(name, value) キー名と値 undefined p.delete('tag', 'x') q=js&page=3&tag=y
delete(name) キー名 undefined p.delete('q') page=3&tag=y
sort() なし undefined p.append('a', '1'); p.sort() a=1&page=3&tag=y
toString() なし クエリ文字列(先頭の ? なし) p.toString() 'a=1&page=3&tag=y'
entries() なし [キー, 値] の反復子 [...p.entries()] [['a','1'],['page','3'],['tag','y']]
keys() なし キーの反復子 [...p.keys()] ['a', 'page', 'tag']
values() なし 値の反復子 [...p.values()] ['1', '3', 'y']
forEach(fn) (value, key) => {} undefined p.forEach((v, k) => ...) 値、キーの順で渡される

覚えておきたい3点 は次のとおりです。

  • 値はすべて文字列 で返ります。数値として使うときは Number() などで変換します。
  • キーがないとき、get は null、getAll は空配列 を返します。
  • set・append・delete・sort は元のオブジェクトを書き換え、戻り値は undefined です。メソッドチェーンはできません。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「JavaScriptでURLパラメータを取得|URLSearchParams早見表」

URL パラメータを取得する

現在のページの URL から取得する

http://127.0.0.1:7560/items?q=js&tag=a&tag=b&utm_source=x#top を Chromium 141 で開いて実行した結果です。

const params = new URLSearchParams(location.search);

console.log(location.search);        // ? を含むクエリ文字列
console.log(params.get('q'));        // 1つ取得
console.log(params.getAll('tag'));   // 同じキーをすべて取得
console.log(params.get('none'));     // ないキー
?q=js&tag=a&tag=b&utm_source=x
js
["a", "b"]
null

location.search の先頭には ? が付いていますが、URLSearchParams は先頭の ? を1つだけ取り除いてくれる ので、そのまま渡せます。new URL(location.href).searchParams.get('q') と書いても同じ 'js' が得られました。

文字列の URL から取得する

現在のページ以外の URL は、URL クラスで解析してから searchParams を使います。

const url = new URL('https://www.example.com/items/list.html?q=js&page=2#sec-1');
console.log(url.searchParams.get('page'));
2

URL 全体を URLSearchParams に直接渡すと、意図と違う結果になります。

console.log([...new URLSearchParams('https://example.com/?a=1&b=2')]);
[ [ 'https://example.com/?a', '1' ], [ 'b', '2' ] ]

最初のキーが https://example.com/?a になりました。URLSearchParams に渡すのは ? 以降だけです。URL 全体を持っているときは、new URL(文字列).searchParams を使います。

数値・真偽値として受け取る

値は文字列なので、型を変えるときは「キーがない場合」と「値が空の場合」を先に決めておくと安心です。

クエリ get('page') Number(...) 補足
page=2 '2' 2
キーなし null 0 Number(null) は 0 になる
page= '' 0 空文字列も 0 になる
page=abc 'abc' NaN parseInt でも NaN

Number(null) が 0 になるため、「指定なし」と「0 が指定された」を区別したいときは、変換の前に null を判定 します。

function getInt(params, name, fallback) {
  const raw = params.get(name);
  if (raw === null || raw.trim() === '') return fallback;
  const n = Number(raw);
  return Number.isInteger(n) ? n : fallback;
}

const params = new URLSearchParams('page=2&size=abc');
console.log(getInt(params, 'page', 1), getInt(params, 'size', 20), getInt(params, 'none', 1));
2 20 1

真偽値は params.get('debug') === 'true' のように文字列と比較します。?debug(値なし)で有効にしたい場合は params.has('debug') を使います。

クエリ文字列の挙動を実行結果で比較する

同じように見えるクエリ文字列でも、書き方で結果が変わります。new URLSearchParams(入力) の中身([...params])と、toString() で文字列に戻した結果を並べました(Node.js 22.22.2)。

入力 解析結果([キー, 値]) toString() ポイント
q=a+b ["q","a b"] q=a+b + はスペースとして読まれる
q=a%20b ["q","a b"] q=a+b %20 もスペース。出力は + になる
q=a%2Bb ["q","a+b"] q=a%2Bb 記号の + を送るには %2B
q=1+1=2 ["q","1 1=2"] q=1+1%3D2 最初の = でキーと値に分かれる
tag=a&tag=b ["tag","a"],["tag","b"] tag=a&tag=b 同名キーは両方残る
q= ["q",""] q= 値は空文字列
flag ["flag",""] flag= 値なしキーも空文字列。出力には = が付く
=v ["","v"] =v キーが空文字列
q=%E6%9D%B1%E4%BA%AC ["q","東京"] q=%E6%9D%B1%E4%BA%AC 日本語は自動でデコード
q=東京 ["q","東京"] q=%E6%9D%B1%E4%BA%AC そのまま渡しても読める
a[]=1&a[]=2 ["a[]","1"],["a[]","2"] a%5B%5D=1&a%5B%5D=2 キー名は a[] のまま。配列にはならない
ids=1,2,3 ["ids","1,2,3"] ids=1%2C2%2C3 カンマ区切りは1つの文字列
q=a&&b=1& ["q","a"],["b","1"] q=a&b=1 空の区切りは無視される
q=%zz ["q","%zz"] q=%25zz 不正な % 表記でも例外にならない
q=a#top ["q","a#top"] q=a%23top # は値の一部として扱われる
??q=1 ["?q","1"] %3Fq=1 取り除かれる ? は先頭の1つだけ
Q=1&q=2 ["Q","1"],["q","2"] Q=1&q=2 大文字と小文字は別のキー

「+」と %20 の違い

URLSearchParams は、読むときに + と %20 の両方をスペースとして扱い、書き出すときはスペースを + にします。 これは HTML フォームの送信形式(application/x-www-form-urlencoded)のルールです。

つまずきやすいのは、値に記号の + を含めたいとき です。文字列を自分で連結して ?q=a+b と書くと、受け取る側ではスペースになります。set や append を使えば、自動で %2B に変換されます。

// 直す前:+ がスペースとして読まれる
console.log(new URLSearchParams('q=C++').get('q'));

// 直した後:set に任せる
const p = new URLSearchParams();
p.set('q', 'C++');
console.log(p.toString(), '→', new URLSearchParams(p.toString()).get('q'));
C  
q=C%2B%2B → C++

直す前は C の後ろにスペースが2つ付いた文字列になっています。

同名キー:get は最初の1つ、getAll はすべて

tag=a&tag=b のように同じキーが複数あるとき、get は最初の値だけ を返します。複数選択のチェックボックスや絞り込み条件では getAll を使います。

操作 tag=a&tag=b に対する結果
get('tag') 'a'
getAll('tag') ['a', 'b']
set('tag', 'x') tag=x(2つとも置き換わり1つになる)
append('tag', 'y') tag=a&tag=b&tag=y
delete('tag') すべて消える
delete('tag', 'a') tag=b(値が一致するものだけ消える)

空の値・値なしキー・キーなしの見分け方

クエリ has('k') get('k')
k=1 true '1'
k= true ''
k true ''
(k なし) false null

k= と k は、URLSearchParams からは区別できません。「キーがあるかどうか」は has、「値が入っているかどうか」は get('k') が空文字列でないかで判定します。if (params.get('k')) と書くと、空の値と「キーなし」の両方が偽になります。

配列(a[]=1&a[]=2)の扱い

a[]=1&a[]=2 という書き方は、一部のサーバー側フレームワークが配列として解釈する慣習で、URLSearchParams にとっては a[] という名前のキーが2つあるだけ です。

const p = new URLSearchParams('a[]=1&a[]=2');
console.log(p.getAll('a[]'), p.getAll('a'));
[ '1', '2' ] []

getAll('a[]') とキー名に角括弧まで含めれば取得できます。配列をどの形式で送るかは、受け取る側の仕様に合わせます。

形式 送り方 読み方
同名キーの繰り返し tag=a&tag=b append('tag', v) を繰り返す getAll('tag')
角括弧つき tag[]=a&tag[]=b append('tag[]', v) を繰り返す getAll('tag[]')
カンマ区切り tag=a,b set('tag', arr.join(',')) get('tag').split(',')

ハッシュ(#)の中のパラメータ

# 以降はクエリ文字列ではないため、location.search や searchParams には含まれません。

const h = new URL('https://example.com/app?x=1#/list?tab=2&sort=new');
console.log(h.search);
console.log(h.hash);
console.log(h.searchParams.get('tab'));

// ハッシュの中の ? 以降を自分で取り出す
const hashParams = new URLSearchParams(h.hash.split('?')[1] ?? '');
console.log(hashParams.get('tab'));
?x=1
#/list?tab=2&sort=new
null
2

#key=value&key2=value2 の形でハッシュ全体がパラメータになっている場合は、先頭の # を取り除いて渡します。

const h2 = new URL('https://example.com/cb#access=abc&state=xyz');
console.log(Object.fromEntries(new URLSearchParams(h2.hash.slice(1))));
{ access: 'abc', state: 'xyz' }

ハッシュの部分はサーバーへ送信されず、ブラウザの中だけで使われます。

URL パラメータをオブジェクトに変換する

Object.fromEntries は同名キーが1つになる

Object.fromEntries(params) は1行で書けて便利ですが、同じキーが複数あると、最後の値だけが残ります。

const q = new URLSearchParams('q=js&tag=a&tag=b&page=2');
console.log(Object.fromEntries(q));
{ q: 'js', tag: 'b', page: '2' }

tag=a がなくなりました。オブジェクトは同じキーを1つしか持てないため、後の値で上書きされます。同名キーがありうる場合は、次のどちらかの関数を使います。

// A. 複数あるキーだけ配列にする
function toObject(params) {
  const obj = {};
  for (const key of new Set(params.keys())) {
    const values = params.getAll(key);
    obj[key] = values.length > 1 ? values : values[0];
  }
  return obj;
}

// B. すべてのキーを配列にそろえる(型が一定になる)
function toObjectAll(params) {
  const obj = {};
  for (const [key, value] of params) (obj[key] ??= []).push(value);
  return obj;
}

console.log(toObject(q));
console.log(toObjectAll(q));
{ q: 'js', tag: [ 'a', 'b' ], page: '2' }
{ q: [ 'js' ], tag: [ 'a', 'b' ], page: [ '2' ] }
方法 同名キー 値の型 向いている場面
Object.fromEntries(params) 最後の1つだけ残る 常に文字列 同名キーが来ないとわかっている
toObject(A) 配列になる 文字列または配列 表示用・ログ用
toObjectAll(B) 配列になる 常に配列 後続の処理で型をそろえたい

A は値が1つのとき文字列、2つ以上のとき配列になるため、受け取る側で型の判定が必要です。「複数ありうるキー」が決まっているなら、そのキーだけ getAll で取り出す のが最も明快です。

URLSearchParams を作る(文字列・オブジェクト・配列から)

渡すもの コード toString() の結果
文字列 new URLSearchParams('q=js&page=2') q=js&page=2
オブジェクト new URLSearchParams({ q: 'js', page: 2 }) q=js&page=2
[キー, 値] の配列 new URLSearchParams([['tag', 'a'], ['tag', 'b']]) tag=a&tag=b
値が配列のオブジェクト new URLSearchParams({ tag: ['a', 'b'] }) tag=a%2Cb(カンマ区切りの1つの値)
undefined・null を含むオブジェクト new URLSearchParams({ a: undefined, b: null, c: '' }) a=undefined&b=null&c=

下の2行は注意が必要です。オブジェクトの値が配列だと a,b という1つの文字列になり、undefined や null は文字列の 'undefined'・'null' として入ります。 検索フォームの条件など、空の項目がありうるオブジェクトから作るときは、先に取り除きます。

// 空の項目(undefined・null・空文字列)を除いて作る
const clean = (obj) => new URLSearchParams(
  Object.entries(obj)
    .filter(([, v]) => v != null && v !== '')
    .map(([k, v]) => [k, String(v)])
);
console.log(clean({ q: 'js', page: 1, sort: undefined, tag: null, empty: '' }).toString());

// 配列の値は同名キーとして展開する
const multi = new URLSearchParams();
for (const [k, v] of Object.entries({ q: 'js', tag: ['a', 'b'] })) {
  for (const x of [].concat(v)) multi.append(k, x);
}
console.log(multi.toString());
q=js&page=1
q=js&tag=a&tag=b

パラメータを追加・変更・削除して URL を書き換える

URL オブジェクトの searchParams を操作する

url.searchParams を変更すると、URL 本体(url.href)にもすぐ反映されます。文字列の連結や正規表現を使わずに済みます。

const u = new URL('https://example.com/items?q=js');
u.searchParams.set('page', '2');             // 追加(あれば上書き)
u.searchParams.append('tag', '新着 品');     // 追加(同名でも足す)
u.searchParams.delete('q');                  // 削除
console.log(u.href);
https://example.com/items?page=2&tag=%E6%96%B0%E7%9D%80+%E5%93%81

日本語とスペースは自動でエンコードされました。

やりたいこと メソッド 同名キーがすでにあるとき
値を1つに決める set(name, value) すべて置き換えて1つにする
値を足す append(name, value) 既存の値を残して追加する
キーごと消す delete(name) すべて消える
特定の値だけ消す delete(name, value) 一致する組だけ消える
すべて消す url.search = '' クエリ文字列がなくなる

history.replaceState でページを再読み込みせずに URL を変える

history.replaceState(null, '', url) を使うと、ページを再読み込みせずにアドレスバーの URL を書き換えられます。 絞り込み条件を URL に反映する、計測用のパラメータを取り除く、といった用途に使えます。

http://127.0.0.1:7560/items?q=js&tag=a&tag=b&utm_source=x#top を開いた状態で実行した結果です(Chromium 141)。

const url = new URL(location.href);
url.searchParams.delete('utm_source');
url.searchParams.set('page', '2');

history.replaceState(null, '', url);   // 履歴を増やさずに置き換える
console.log(location.href);

url.searchParams.set('page', '3');
history.pushState({ page: 3 }, '', url);   // 履歴に1件追加する
console.log(location.href);
http://127.0.0.1:7560/items?q=js&tag=a&tag=b&page=2#top
http://127.0.0.1:7560/items?q=js&tag=a&tag=b&page=3#top
メソッド 履歴(history.length) ブラウザの「戻る」
history.replaceState(state, '', url) 増えない(実測:変化なし) 書き換え前の URL には戻らない
history.pushState(state, '', url) 1件増える(実測:+1) 書き換え前の URL に戻る
location.search = '?page=2' 1件増える(今回は未実測) ページが再読み込みされる

入力のたびに変わる条件は replaceState、ページ送りのように「戻る」で前の状態に戻したい操作は pushState が向いています。pushState で追加した履歴を「戻る」でたどると、window で popstate イベントが発生するので、そこで URL を読み直して画面を更新します。

その他、確認できたことをまとめます。

操作 結果(Chromium 141)
クエリをすべて消す:u.search = ''; history.replaceState(null, '', u) http://127.0.0.1:7560/items#top
相対指定:history.replaceState(null, '', '?sort=new') http://127.0.0.1:7560/items?sort=new(ハッシュは消える)
別のオリジンを指定 SecurityError(同じオリジンの URL だけ指定できる)

相対指定の '?sort=new' ではハッシュ(#top)がなくなりました。ハッシュを残したいときは、上のコードのように new URL(location.href) から作った URL オブジェクトを渡します。

コピペ用:現在の URL のパラメータを更新する関数

/**
 * 現在の URL のパラメータを更新する。値が null・undefined・'' のキーは削除する。
 * 例: updateQuery({ page: 2, sort: null })
 */
function updateQuery(changes, { push = false } = {}) {
  const url = new URL(location.href);
  for (const [key, value] of Object.entries(changes)) {
    if (value == null || value === '') {
      url.searchParams.delete(key);
    } else if (Array.isArray(value)) {
      url.searchParams.delete(key);
      for (const v of value) url.searchParams.append(key, String(v));
    } else {
      url.searchParams.set(key, String(value));
    }
  }
  history[push ? 'pushState' : 'replaceState'](null, '', url);
  return url;
}

/items?q=js&sort=new#top を開いた状態で updateQuery({ page: 2, sort: null, tag: ['a', 'b'] }) を呼ぶと、URL は /items?q=js&page=2&tag=a&tag=b#top になり、履歴の件数は変わりませんでした。続けて updateQuery({ page: 3 }, { push: true }) を呼ぶと page=3 になって履歴が1件増え、「戻る」で page=2 の URL に戻って popstate が1回発生しました(Chromium 141)。

fetch にパラメータを付ける

API の URL は、URL オブジェクトに searchParams を設定して作ると、エンコードを気にせずに済みます。 リクエストをそのまま返す検証用サーバー(/echo)で、サーバーが受け取った内容を確認しました(Node.js 22.22.2)。

const api = new URL('http://127.0.0.1:7560/echo');
api.search = new URLSearchParams({ q: '東京 タワー', page: 1, tag: 'a&b' });
console.log(api.href);

const res = await fetch(api);   // URL オブジェクトをそのまま渡せる
console.log(await res.json());
http://127.0.0.1:7560/echo?q=%E6%9D%B1%E4%BA%AC+%E3%82%BF%E3%83%AF%E3%83%BC&page=1&tag=a%26b
{
  url: '/echo?q=%E6%9D%B1%E4%BA%AC+%E3%82%BF%E3%83%AF%E3%83%BC&page=1&tag=a%26b',
  q: [ [ 'q', '東京 タワー' ], [ 'page', '1' ], [ 'tag', 'a&b' ] ]
}

値に含まれる & が %26 に変換され、サーバー側では a&b という1つの値として届いています。文字列の連結で '?tag=' + tag と書いた場合、a&b は tag=a と b の2つに分かれてしまいます。

テンプレートリテラルに埋め込むこともできます(自動で toString() が呼ばれます)。

const qs = new URLSearchParams([['tag', 'a'], ['tag', 'b']]);
const res2 = await fetch(`http://127.0.0.1:7560/echo?${qs}`);
console.log(await res2.json());
{ url: '/echo?tag=a&tag=b', q: [ [ 'tag', 'a' ], [ 'tag', 'b' ] ] }

fetch の body に URLSearchParams を渡すと、フォームと同じ形式(application/x-www-form-urlencoded)で POST できます。fetch 全般は別記事「JavaScript fetchの使い方」で扱っています。

URL クラスの各プロパティ

URL を部分ごとに取り出すときは、URL クラスのプロパティを使います。

const u = new URL('https://user:pw@www.example.com:8443/items/list.html?q=js&page=2#sec-1');
プロパティ 値(実行結果) 内容
href https://user:pw@www.example.com:8443/items/list.html?q=js&page=2#sec-1 URL 全体
origin https://www.example.com:8443 プロトコル+ホスト+ポート(読み取り専用)
protocol https: 末尾にコロンが付く
username user
password pw
host www.example.com:8443 ホスト名+ポート
hostname www.example.com ホスト名のみ
port 8443 文字列。既定のポートのときは空文字列
pathname /items/list.html パス
search ?q=js&page=2 ? を含むクエリ文字列。なければ空文字列
searchParams URLSearchParams search と連動するオブジェクト
hash #sec-1 # を含む。なければ空文字列

location オブジェクトも同じ名前のプロパティ(href・origin・pathname・search・hash など)を持っています。

相対 URL は第2引数に基準を渡す

console.log(new URL('../list?x=1', 'https://example.com/a/b/c').href);
console.log(new URL('/list', 'https://example.com/a/b/').href);
new URL('/items?q=1');   // 基準なし
https://example.com/a/list?x=1
https://example.com/list
TypeError: Invalid URL

new URL() は、解析できない文字列を渡すと TypeError を投げます。 相対 URL を扱うときは、ブラウザなら new URL(path, location.origin) のように基準を渡します。

URL.canParse で例外を出さずに確認する

URL.canParse() は、その文字列が URL として解析できるかを真偽値で返す 静的メソッドです。try/catch を書かずに判定できます。

console.log(URL.canParse('https://example.com'));              // 絶対 URL
console.log(URL.canParse('/items?q=1'));                       // 相対 URL(基準なし)
console.log(URL.canParse('/items?q=1', 'https://example.com')); // 相対 URL(基準あり)
console.log(URL.canParse('example.com'));                      // プロトコルなし
console.log(URL.canParse('mailto:a@example.com'));
true
false
true
false
true

mailto: のように http 以外のスキームも true になります。「http または https の URL か」を確かめたいときは、protocol も確認 します。

function isHttpUrl(text) {
  if (!URL.canParse(text)) return false;
  const { protocol } = new URL(text);
  return protocol === 'http:' || protocol === 'https:';
}

Node.js 22.22.2 と Chromium 141 では、解析できないときに null を返す URL.parse() も動作しました(URL.parse('nope') は null)。canParse・parse、および size プロパティや has(name, value)・delete(name, value) の2引数形式は、URLSearchParams の基本メソッドより後に追加された機能です。幅広いブラウザを対象にする場合は、MDN の対応表を確認してから使ってください。

encodeURIComponent との違い

値を URL 用に変換する方法には、URLSearchParams のほかに encodeURIComponent があります。同じ文字列 a b+c&d=e/f?g#h 東京 を変換した結果を比べました。

方法 結果
URLSearchParams の set → toString a+b%2Bc%26d%3De%2Ff%3Fg%23h+%E6%9D%B1%E4%BA%AC
encodeURIComponent a%20b%2Bc%26d%3De%2Ff%3Fg%23h%20%E6%9D%B1%E4%BA%AC
encodeURI a%20b+c&d=e/f?g#h%20%E6%9D%B1%E4%BA%AC
比べる点 URLSearchParams encodeURIComponent encodeURI
スペース + %20 %20
& = ? # / + 変換する 変換する 変換しない
! ~ ' ( ) 変換する(%21 など) 変換しない 変換しない
用途 クエリ文字列全体の組み立て・解析 パスの一部など、1つの値の変換 URL 全体(区切り文字を残す)

クエリ文字列を扱うなら URLSearchParams に任せるのが簡単 です。キーと値の組み立て、& での連結、エンコード、デコードまでを一括で行います。encodeURIComponent は、/users/${encodeURIComponent(name)} のように パスの一部に値を埋め込むとき に使います。encodeURI は & や = を変換しないため、パラメータの値の変換には向きません。

読む側にも違いがあります。

console.log(decodeURIComponent('a+b'));               // + はそのまま
console.log(new URLSearchParams('q=a+b').get('q'));   // + はスペースに
decodeURIComponent('%zz');                            // 不正な表記
console.log(new URLSearchParams('q=%zz').get('q'));
a+b
a b
URIError: URI malformed
%zz

decodeURIComponent は + をスペースに戻さず、不正な % 表記では URIError を投げます。URLSearchParams は + をスペースとして読み、不正な表記はそのまま文字列として返しました。クエリ文字列を split('&') と decodeURIComponent で自作するより、URLSearchParams を使うほうが取りこぼしが少なくなります。

Node.js でも同じように使える

URL と URLSearchParams は Node.js でもグローバルに用意されており、import なしで使えます(この記事の実行結果の多くは Node.js 22.22.2 で取得したものです)。

// server.mjs(node server.mjs で実行)
import http from 'node:http';

http.createServer((req, res) => {
  // req.url は '/echo?q=js' のようなパス部分だけなので、基準を渡す
  const url = new URL(req.url, 'http://localhost');
  const q = url.searchParams.get('q');
  const tags = url.searchParams.getAll('tag');
  res.writeHead(200, { 'content-type': 'application/json' });
  res.end(JSON.stringify({ path: url.pathname, q, tags }));
}).listen(7560, '127.0.0.1');

/echo?q=js&tag=a&tag=b にアクセスすると {"path":"/echo","q":"js","tags":["a","b"]} が返りました。req.url はホスト名を含まないため、new URL(req.url) だけでは Invalid URL になります。第2引数に基準の URL を渡すのがポイントです。

Node.js には以前から node:querystring モジュールもあります。同名キーの扱いが異なります。

import querystring from 'node:querystring';
console.log(querystring.parse('tag=a&tag=b&q=1'));
[Object: null prototype] { tag: [ 'a', 'b' ], q: '1' }

querystring.parse は同名キーを自動で配列にしたオブジェクトを返します。ブラウザと同じコードを使いたい場合は URLSearchParams を選ぶと、動作をそろえられます。

よくあるつまずきと確認ポイント

症状 原因 対応
get の結果が null キー名の大文字小文字が違う/ハッシュの中にある キー名を確認する/location.hash から取り出す
値の + がスペースになる + はスペースとして読まれる 送る側で set を使う(%2B になる)
同名キーが1つしか取れない get や Object.fromEntries を使っている getAll を使う
page が 0 になる Number(null) や Number('') は 0 null・空文字列を先に判定する
URL に undefined という文字が入る オブジェクトの値が undefined のまま渡された 空の項目を除いてから作る
Invalid URL 相対 URL を基準なしで渡した 第2引数に基準を渡す/URL.canParse で確認
最初のキーが URL ごと入る URL 全体を URLSearchParams に渡した new URL(文字列).searchParams を使う
toString() に ? が付かない 仕様どおり(先頭の ? は含まない) '?' + params と自分で付ける/url.search を使う

よくある質問

Q. location.search と URLSearchParams はどう違いますか? A. location.search は ?q=js&page=2 のような「文字列」です。URLSearchParams はその文字列を解析して、get や set で読み書きできるようにした「オブジェクト」です。new URLSearchParams(location.search) と組み合わせて使います。

Q. URLSearchParams を変更したのに、アドレスバーの URL が変わりません。 A. new URLSearchParams(location.search) で作ったオブジェクトは location と連動していません。変更後に history.replaceState(null, '', url) などで反映します。new URL(location.href) から作った url.searchParams を変更し、その url を渡す形が書きやすくなります。

Q. パラメータの順番は保たれますか? A. 追加した順番が保たれます。順番をそろえたいとき(キャッシュのキーにする、比較するなど)は sort() を呼ぶと、キー名の順に並びます。同じキーの値どうしの順番は変わりません。

Q. URL パラメータの値をそのまま画面に表示してもよいですか? A. URL パラメータは利用者が自由に書き換えられる値です。画面に出すときは innerHTML ではなく textContent を使い、数値や選択肢は想定した範囲に入っているかを確認してから使うと安心です。

まとめ

  • 取得は new URLSearchParams(location.search).get('キー')。値は文字列、キーがなければ null が返ります。
  • 同名キーは getAll で配列として取得 します。get や Object.fromEntries では1つしか得られません。
  • + と %20 はどちらもスペースとして読まれ、書き出すときは + になります。記号の + は set に任せると %2B になります。
  • 追加・変更は set、足すときは append、削除は delete。url.searchParams を操作すると URL 本体にも反映されます。
  • history.replaceState(null, '', url) で、再読み込みなしにアドレスバーの URL を書き換え られます。履歴を残すなら pushState です。
  • fetch には URL オブジェクトをそのまま渡せます。エンコードは URLSearchParams に任せられます。
  • URL として解析できるかは URL.canParse() で確認 でき、Node.js でも同じコードが動きます。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「JavaScriptでURLパラメータを取得|URLSearchParams早見表」

株式会社RJCでは、URLSearchParams をはじめとする Web 標準 API の使い方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?