URLSearchParams
Builds and parses application/x-www-form-urlencoded query strings from a string, object, or pairs.
new URLSearchParams()
new URLSearchParams(string)
new URLSearchParams(obj)
new URLSearchParams(iterable)- Added in Node.js v7.5.0, v6.13.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$stringoptional | string | A query string. A leading '?', if present, is ignored. (Constructor overload.) |
$objoptional | Object | An object representing a collection of key-value pairs. The key and value of each property of obj are always coerced to strings. (Constructor overload, added in v7.10.0, v6.13.0.) |
$iterableoptional | Iterable | An iterable object whose elements are key-value pairs, in a way that is similar to Map's constructor. (Constructor overload, added in v7.10.0, v6.13.0.) |
Return value
URLSearchParamsA mutable, iterable collection of name-value pairs that serializes to a query string.
Errors
Throws a TypeError when an iterable's elements are not two-element key-value pairs.
Changelog
- PHP v10.0.0The class is now available on the global object, so it can be used without requiring node:url.
- PHP v7.10.0, v6.13.0The object and iterable constructor forms were added; earlier versions accepted only a query string.
URLSearchParams is the standard way to handle query strings in Node. It is a global (since Node 10) and it is the intended replacement for the legacy querystring module, which does not follow the same encoding rules.
Choosing a constructor form
Pass a string to parse an existing query — a leading ? is stripped for you, so you can hand it url.search directly. Pass an object when building from known fields; keys and values are coerced to strings, so numbers and booleans are fine. Pass an array of pairs (or any iterable, including another URLSearchParams) when a key must repeat — an object literal cannot express id=1&id=2.
What trips people up
It is a multimap, not a plain object. Two entries can share a name, which is why get() returns only the first and getAll() returns the list. Iterating with for...of yields [name, value] pairs in insertion order, so Object.fromEntries() will quietly drop duplicates.
Values are always strings. new URLSearchParams({ active: true }).get('active') is the string 'true', and null or undefined become the literal text 'null' / 'undefined' rather than being omitted.
Example
When to use it
- Build a form-encoded request body for fetch without hand-rolling encoding
- Parse an incoming request's query string into something you can read keys from
- Construct a paginated API query from an options object
- Merge default parameters with caller-supplied overrides before issuing a request
Frequently asked questions
Should I use URLSearchParams or the querystring module?
Use URLSearchParams — querystring is legacy, and only URLSearchParams follows the WHATWG/form-urlencoded rules that browsers and servers expect. It is also a global, so there is nothing to import.
How do I create a query string with the same key twice?
Use the array-of-pairs constructor, new URLSearchParams([['id','1'],['id','2']]), or call append() twice. An object literal cannot hold duplicate keys, so the object form can never produce them.
How do I turn a query string into an object in Node?
Object.fromEntries(new URLSearchParams(str)) gives you a plain object, but it keeps only the last value of any repeated key. Loop over the keys with getAll() if duplicates matter.
More examples
Merging defaults with caller overrides
set replaces a default rather than adding a second copy, and the explicit undefined check is what stops per_page from becoming the string 'undefined'. The last line shows the space in 'node url' serialized as +.
function buildQuery(overrides = {}) {
const params = new URLSearchParams({ page: 1, per_page: 25, order: 'desc' });
for (const [key, value] of Object.entries(overrides)) {
if (value !== undefined && value !== null) params.set(key, value);
}
return params.toString();
}
console.log(buildQuery());
console.log(buildQuery({ page: 3, order: 'asc' }));
console.log(buildQuery({ q: 'node url', per_page: undefined }));Converting to and from a plain object
Object.fromEntries keeps only the last value for tag, losing 'js'. The manual grouping loop preserves repeated keys as an array, which is what you usually want when parsing a real query string.
const params = new URLSearchParams('tag=js&tag=node&page=2');
console.log(Object.fromEntries(params));
console.log(params.getAll('tag'));
const grouped = {};
for (const key of new Set(params.keys())) {
const all = params.getAll(key);
grouped[key] = all.length > 1 ? all : all[0];
}
console.log(grouped);Using it as a form-encoded request body
Passing a URLSearchParams instance as a fetch body makes the runtime set the form-urlencoded Content-Type automatically. Here the request itself is commented out because the sandbox has no network, but the encoded body is exactly what would be sent.
const body = new URLSearchParams({
grant_type: 'client_credentials',
scope: 'read write',
client_id: 'abc 123'
});
console.log(body.toString());
console.log('Content-Type: application/x-www-form-urlencoded');
// fetch('https://auth.example.com/token', { method: 'POST', body });
console.log([...body.keys()]);
Discussion