How to migrate from querystring to URLSearchParams in Node.js
Photo by Maksim Shutov (@maksimshutov)

How to migrate from querystring to URLSearchParams in Node.js

Written on

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.js 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.

API Result
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.js 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.

API Result
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.

Fork me on GitHub