How to migrate from querystring to URLSearchParams in Node.js

Vladimír Gorej
4 min readOct 14, 2021

--

Photo by Maksim Shutov (@maksimshutov)
Photo by Maksim Shutov (@maksimshutov)

Node.js marked the querystring as legacy API in version 14.x, and recommends using URLSearchParams. But doesn’t give us any clue how to actually migrate. This article fills this void and provides a migration guide.

querystring

querystring API is quite simple and straightforward:

querystring.parse()
Parses a URL query string into a collection of key and value pairs. querystring.decode() is an alias.

querystring.stringify()
Produces a URL query string from a given obj by iterating through the object’s “own properties”. querystring.encode() is an alias.

querystring.escape()
Performs URL percent-encoding on the given string in a manner that is optimized for the specific requirements of URL query strings. Is used by querystring.stringify().

querystring.unescape()
Performs decoding of URL percent-encoded characters on the given string. Is used by querystring.parse().

Parse a URL query string

const { parse } = require('querystring');

parse('foo=bar&abc=xyz&abc=123');

// returns
// {
// foo: 'bar',
// abc: ['xyz', '123']
// }

Produce a URL query string

const { stringify } = require('querystring');

stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' });

// returns 'foo=bar&baz=qux&baz=quux&corge='

Perform URL percent-encoding on the given string

const { escape } = require('querystring');

escape('str1 str2');

// returns 'str1%20str2'

Perform decoding of URL percent-encoded characters on the given string

const { unescape } = require('querystring'); escape('str1%20str2');  // returns 'str1 str2'

URLSearchParams

What’s the actual difference between querystring vs. URLSearchParams?

“The WHATWG URLSearchParams interface and the querystring module have similar purpose, but the purpose of the querystring module is more general, as it allows the customization of delimiter characters.” — Node 14.x documentation

Working with URIs/URLs in JavaScript was always confusing. WHATWG URL specification finally standardizes the term URL and provides a standardized way how we work with URLs. URLSearchParams is part of this specification. Now let’s migrate our previous querystring examples to URLSearchParams API.

Parse a URL query string

const params = new URLSearchParams('foo=bar&abc=xyz&abc=123');
// URLSearchParams { 'foo' => 'bar', 'abc' => 'xyz', 'abc' => '123' }

params.get('foo'); // 'bar'
params.getAll('abc') // ['xyz', '123']

Produce a URL query string

new URLSearchParams({ foo: 'bar', baz: ['qux', 'quux'], corge: '' }).toString();
// returns 'foo=bar&baz=qux%2Cquux&corge='

As we can see, what we got is a different result compared to querystring.

querystring: foo=bar&baz=qux&baz=quux&corge=

URLSearchParams: foo=bar&baz=qux%2Cquux&corge=

“Unlike querystring module, duplicate keys in the form of array values are not allowed. Arrays are stringified using array.toString(), which simply joins all array elements with commas.” — Node 14.x documentation

To get the result compatible with querystring, we have to initialize URLSearchParams with a list of tuples instead of object:

new URLSearchParams([
['foo', 'bar'],
['baz', 'qux'],
['baz', 'quux'],
['corge', ''],
]).toString();
// returns 'foo=bar&baz=qux&baz=quux&corge='

Perform URL percent-encoding on the given string

There is no low-level API for encoding simple strings in URLSearchParams. We have to be a little creative to achieve URL encoding. Below are 2 different yet functionally equivalent ways how to achieve the same result.

new URLSearchParams([['', 'str1 str2']]).toString().slice(1);
new URLSearchParams({'': 'str1 str2'}).toString().slice(1);
// returns 'str1+str2'

Again, we can see what we got is a different result compared to querystring. WHATWG URL specification is now precise about how various segments of URL gets encoded. Spaces in query parameters are now plus-sign (+) encoded, compared to percent-encoded spaces in the path segment of the URL. This currently accepted behavior is backward incompatible with querystring, and there is nothing you can do about it except to take it.

querystring: str1%20str2

URLSearchParams: str1+str2

Perform decoding of URL percent-encoded characters on the given string

There is no low-level API for decoding a simple string in URLSearchParams. Again we have to be creative to achieve URL decoding on the given string.

const urlPlusEncodedString = 'str1+str2';

new URLSearchParams(`=${urlPlusEncodedString}`).get('');
// returns 'str1 str2'

URLSearchParams will handle percent-encoded spaces as well:

const urlPercentEncodedString = 'str1%20str2';

new URLSearchParams(`=${urlPercentEncodedString}`).get('');
// returns 'str1 str2'

Other differences

Handling of question mark character

URLSearchParams removes question mark character from the start of the URL query string. querystring keeps it and makes it part of the key name.

const { parse } = require('querystring');

parse('?foo=bar'); // returns { '?foo': 'bar' }
new URLSearchParams('?foo=bar'); // returns URLSearchParams { 'foo' => 'bar' }

Converting URLSearchParams object to Plain JavaScript Object

URLSearchParams object is a class instance. It is not a classical Plain JavaScript Object (POJO). To transform URLSearchParam instance into a POJO, use the following recipe.

const params = new URLSearchParams('foo=bar'); 
const paramsPOJO = Object.fromEntries(params.entries()); // { 'foo': 'bar' }

Have you found other differences? I’d be happy to add them to this list.

Closing words

querystring is a legacy Node.js API that shouldn’t be used anymore. New code should only use URLSearchParams, which is part of WHATWG URL specification. Old code using querystring can easily be migrated to URLSearchParams using this guide. The only backward incompatibility between the two APIs is that URLSearchParams use plus-encoded spaces instead of percent-encoded spaces in querystring.

Originally published at https://vladimirgorej.com on October 14, 2021.

--

--

Vladimír Gorej

Certified Open Source Software Engineer, OSS contributor, author and content creator. OSS is my passion and my profession. GitHub Star.