querystring.stringify()

Serializes an object's own properties into a percent-encoded URL query string.

Signature
querystring.stringify(obj[, sep[, eq[, options]]])
  • Added in Node.js v0.1.25

Parameters

ParameterTypeDescription
$obj<Object>The object to serialize into a URL query string.
$sepoptional<string>= '&'The substring used to delimit key and value pairs in the query string.
$eqoptional<string>= '='The substring used to delimit keys and values in the query string.
$optionsoptional<Object>Options controlling encoding.
$options.encodeURIComponentoptional<Function>= querystring.escape()The function to use when converting URL-unsafe characters to percent-encoding in the query string.

Return value

<string>A query string built from the object's own enumerable properties, with no leading '?'.

Errors

Throws a URIError (ERR_INVALID_URI) when a key or value contains a lone surrogate, because the default encoder is querystring.escape().

This is the write half of the module. It walks the object's own enumerable properties — inherited ones are skipped — and joins them into key=value pairs. The result never carries a leading ?, so you concatenate it yourself.

What it will and will not serialize

Strings, finite numbers, bigints and booleans are serialized, as are arrays of those. An array becomes the key repeated: { baz: ['qux', 'quux'] } is baz=qux&baz=quux, not baz[]= or a JSON blob. If your server expects PHP-style bracket notation you must build that yourself.

Everything else — null, undefined, nested objects, NaN, Infinity, symbols — is coerced to an empty string, so { a: null } becomes a=. Nothing is dropped and nothing throws, which is why a typo that produces undefined shows up as an empty parameter on the wire rather than as an error. Filter the object first if empty keys are unacceptable.

When to reach for it

Use it to build a request URL or an application/x-www-form-urlencoded body, and to round-trip with querystring.parse(). For browser-compatible code prefer new URLSearchParams(obj).toString(), which encodes spaces as + where this function emits %20.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Building the query portion of a URL before passing it to fetch or http.request
  • Encoding an application/x-www-form-urlencoded POST body from a plain object
  • Producing a stable, shareable link that captures the current filter state
  • Emitting repeated parameters for a multi-select filter from a JavaScript array

Frequently asked questions

How do I turn an array into query parameters in Node?

Pass the array as a value and it becomes the key repeated once per element — { tag: ['a','b'] } serializes to tag=a&tag=b. There is no built-in option for PHP-style tag[]=a or comma-joined values; build those strings yourself if the server requires them.

Why does querystring.stringify turn undefined into an empty parameter?

Because only strings, finite numbers, bigints, booleans and arrays of those are serialized, and every other value is coerced to an empty string rather than skipped. Filter the object with Object.entries().filter(...) before serializing if you need those keys omitted.

Does querystring.stringify add a question mark?

No, it returns only the pairs, so you must write '?' + querystring.stringify(obj) yourself. Adding the '?' twice is a common cause of a first key literally named '?foo' on the receiving end.

More examples

Building a request URL and a form body

The same call produces both a URL query and a form body, because they share a grammar. Note content-length is measured with Buffer.byteLength, not body.length: percent-encoding keeps the string ASCII, but that is a habit worth having when a custom encoder is in play.

Example · javascript
const https = require('node:https');
const querystring = require('node:querystring');

// 1. Query string for a GET.
const query = querystring.stringify({
  q: 'node streams',
  tag: ['http', 'perf'],   // repeated key
  page: 1,
});
console.log(`/search?${query}`);
// /search?q=node%20streams&tag=http&tag=perf&page=1

// 2. The exact same encoding is what a form body needs.
const body = querystring.stringify({ user: 'ada', note: 'hi there' });

const req = https.request('https://example.com/notes', {
  method: 'POST',
  headers: {
    'content-type': 'application/x-www-form-urlencoded',
    'content-length': Buffer.byteLength(body),
  },
});
req.end(body);

Dropping empty values before serializing

The first line is the trap: unset fields do not vanish, they arrive at the server as empty strings, which a handler using `if (params.owner)` will treat differently from an absent key. Filtering first — while carefully keeping false and 0, which are meaningful — produces the URL most APIs expect.

Example · javascript
const querystring = require('node:querystring');

const filters = {
  status: 'open',
  owner: undefined,   // not set by the user
  label: null,
  archived: false,
  page: 1,
};

console.log(querystring.stringify(filters));
// status=open&owner=&label=&archived=false&page=1

// Keep booleans and zeros, drop only null/undefined/''.
const clean = Object.fromEntries(
  Object.entries(filters).filter(([, v]) => v !== undefined && v !== null && v !== ''),
);

console.log(querystring.stringify(clean));
// status=open&archived=false&page=1

Swapping in a different encoder

Passing null for sep and eq keeps the defaults while reaching the options argument. The output makes the one visible encoding difference between this module and URLSearchParams concrete: %20 versus +. Both decode back to a space, but byte-for-byte comparisons, signatures and cache keys will disagree.

Example · javascript
const querystring = require('node:querystring');

// Some legacy endpoints want '+' for spaces, like a browser form post.
const plusEncode = (str) => querystring.escape(str).replace(/%20/g, '+');

console.log(querystring.stringify({ name: 'Ada Lovelace' }));
// name=Ada%20Lovelace

console.log(
  querystring.stringify({ name: 'Ada Lovelace' }, null, null, {
    encodeURIComponent: plusEncode,
  }),
);
// name=Ada+Lovelace

// For comparison, the standardized API always uses '+'.
console.log(new URLSearchParams({ name: 'Ada Lovelace' }).toString());
// name=Ada+Lovelace

Discussion

  • Be the first to comment on this lesson.