Tutorials Logic, IN info@tutorialslogic.com

Node.js Query Strings: URLSearchParams and querystring Compatibility

What is the Querystring Module?

Query parameters are the encoded key-value pairs after ? in a URL. For normal web-facing code, parse the request with URL and read its searchParams collection. URLSearchParams preserves repeated keys and follows the web-platform URL model used by fetch and browsers.

The built-in node:querystring module remains stable but is not standardized. Keep it for compatibility when an existing protocol depends on its configurable separators, maximum-key behavior, null-prototype object result, or exact escaping rules. It is not the first choice for new URL handling.

Including the Querystring Module

Require Querystring Module

Require Querystring Module
// Legacy approach (querystring module)
const querystring = require('querystring');

// Modern approach (URLSearchParams - recommended)
const { URLSearchParams } = require('url');
// Or in Node.js 10+, URLSearchParams is global
const params = new URLSearchParams();

Querystring Module Methods

The querystring module provides four main methods for working with query strings:

Method Description Example
parse(str) Parses a URL query string into a key-value object parse('a=1&b=2') → {a:'1', b:'2'}
stringify(obj) Converts an object into a URL query string stringify({a:1, b:2}) → 'a=1&b=2'
escape(str) URL-encodes a string (percent-encoding) escape('hello world') → 'hello%20world'
unescape(str) URL-decodes a string unescape('hello%20world') → 'hello world'

1. querystring.parse() - Parse Query String to Object

The parse() method converts a query string into a JavaScript object. It automatically handles URL decoding and splits parameters by & and =.

querystring.parse() Examples

querystring.parse() Examples
const qs = require('querystring');

// Basic parsing
const parsed1 = qs.parse('name=Alice&age=25&city=Delhi');
console.log(parsed1);
// Output: { name: 'Alice', age: '25', city: 'Delhi' }

// Parsing with URL-encoded characters
const parsed2 = qs.parse('name=John%20Doe&email=john%40example.com');
console.log(parsed2);
// Output: { name: 'John Doe', email: 'john@example.com' }

// Parsing with multiple values for same key
const parsed3 = qs.parse('color=red&color=blue&color=green');
console.log(parsed3);
// Output: { color: ['red', 'blue', 'green'] }

// Custom separator and assignment operator
const parsed4 = qs.parse('name:Alice;age:25', ';', ':');
console.log(parsed4);
// Output: { name: 'Alice', age: '25' }

// Parsing from a full URL (extract query string first)
const url = 'https://example.com/search?q=nodejs&page=2&limit=10';
const queryString = url.split('?')[1];
const parsed5 = qs.parse(queryString);
console.log(parsed5);
// Output: { q: 'nodejs', page: '2', limit: '10' }

2. querystring.stringify() - Convert Object to Query String

The stringify() method converts a JavaScript object into a URL query string format. It automatically handles URL encoding for special characters.

querystring.stringify() Examples

querystring.stringify() Examples
const qs = require('querystring');

// Basic stringification
const obj1 = { name: 'Bob', role: 'admin', active: true };
const str1 = qs.stringify(obj1);
console.log(str1);
// Output: name=Bob&role=admin&active=true

// With special characters (auto URL-encoded)
const obj2 = { name: 'John Doe', email: 'john@example.com' };
const str2 = qs.stringify(obj2);
console.log(str2);
// Output: name=John%20Doe&email=john%40example.com

// With array values
const obj3 = { colors: ['red', 'blue', 'green'] };
const str3 = qs.stringify(obj3);
console.log(str3);
// Output: colors=red&colors=blue&colors=green

// Custom separator and assignment operator
const obj4 = { name: 'Alice', age: 25 };
const str4 = qs.stringify(obj4, ';', ':');
console.log(str4);
// Output: name:Alice;age:25

// Building a complete URL
const baseUrl = 'https://api.example.com/search';
const params = { q: 'node.js', page: 1, limit: 20 };
const fullUrl = `${baseUrl}?${qs.stringify(params)}`;
console.log(fullUrl);
// Output: https://api.example.com/search?q=node.js&page=1&limit=20

3. querystring.escape() and unescape()

These methods handle URL encoding and decoding. The escape() method converts special characters to percent-encoded format, while unescape() reverses the process.

Escape and Unescape Examples

Escape and Unescape Examples
const qs = require('querystring');

// Escape special characters
console.log(qs.escape('hello world'));        // hello%20world
console.log(qs.escape('user@example.com'));   // user%40example.com
console.log(qs.escape('a+b=c'));              // a%2Bb%3Dc
console.log(qs.escape('100% complete'));      // 100%25%20complete

// Unescape encoded strings
console.log(qs.unescape('hello%20world'));    // hello world
console.log(qs.unescape('user%40example.com')); // user@example.com
console.log(qs.unescape('a%2Bb%3Dc'));        // a+b=c

// Note: parse() and stringify() automatically call escape/unescape
const obj = { message: 'Hello World!' };
const encoded = qs.stringify(obj);
console.log(encoded);  // message=Hello%20World!
const decoded = qs.parse(encoded);
console.log(decoded);  // { message: 'Hello World!' }

Modern Alternative: URLSearchParams (Recommended)

The URLSearchParams API is the modern, standard way to work with query strings in Node.js 10+ and all modern browsers. It provides a cleaner, more intuitive API with additional features.

URLSearchParams - Modern Approach

URLSearchParams - Modern Approach
// URLSearchParams is globally available in Node.js 10+
const { URL, URLSearchParams } = require('url');

// Parse query string
const params1 = new URLSearchParams('name=Alice&age=25&city=Delhi');
console.log(params1.get('name'));   // Alice
console.log(params1.get('age'));    // 25
console.log(params1.has('city'));   // true

// Build query string from object
const params2 = new URLSearchParams({ name: 'Bob', page: 1 });
console.log(params2.toString());    // name=Bob&page=1

// Add, update, delete parameters
params2.append('limit', 20);        // Add new parameter
params2.set('page', 2);             // Update existing parameter
params2.delete('name');             // Remove parameter
console.log(params2.toString());    // page=2&limit=20

// Iterate over parameters
const params3 = new URLSearchParams('a=1&b=2&c=3');
for (const [key, value] of params3) {
    console.log(`${key}: ${value}`);
}
// Output: a: 1, b: 2, c: 3

// Use with URL module
const url = new URL('https://example.com/search?q=nodejs&page=2');
console.log(url.searchParams.get('q'));      // nodejs
console.log(url.searchParams.get('page'));   // 2

// Modify URL parameters
url.searchParams.set('page', 3);
url.searchParams.append('limit', 10);
console.log(url.href);
// https://example.com/search?q=nodejs&page=3&limit=10

// Convert to object (requires manual conversion)
const paramsObj = Object.fromEntries(url.searchParams);
console.log(paramsObj);
// { q: 'nodejs', page: '3', limit: '10' }

Practical Use Cases

Real-World Examples

Real-World Examples
const http = require('http');
const url = require('url');
const querystring = require('querystring');

// Example 1: Parse query parameters in HTTP server
const server = http.createServer((req, res) => {
    const parsedUrl = url.parse(req.url);
    const query = querystring.parse(parsedUrl.query);

    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({
        path: parsedUrl.pathname,
        params: query
    }));
});

server.listen(3000);
// Visit: http://localhost:3000/search?q=nodejs&page=2

// Example 2: Build API request URLs
function buildApiUrl(endpoint, params) {
    const baseUrl = 'https://api.example.com';
    const queryString = querystring.stringify(params);
    return `${baseUrl}${endpoint}?${queryString}`;
}

const apiUrl = buildApiUrl('/users', {
    role: 'admin',
    active: true,
    limit: 50
});
console.log(apiUrl);
// https://api.example.com/users?role=admin&active=true&limit=50

// Example 3: Parse form data (application/x-www-form-urlencoded)
const formData = 'username=alice&password=secret123&remember=on';
const parsed = querystring.parse(formData);
console.log(parsed);
// { username: 'alice', password: 'secret123', remember: 'on' }

Querystring vs URLSearchParams Comparison

Feature querystring URLSearchParams
Status Legacy (deprecated) Modern standard (recommended)
Availability Node.js built-in Node.js 10+ and browsers
Parse query string parse(str) new URLSearchParams(str)
Build query string stringify(obj) params.toString()
Get parameter Access object property params.get(key)
Set parameter Modify object params.set(key, value)
Iteration Use Object methods Built-in iterator support
Integration Standalone Works with URL module

Query Parser Choice

Node.js offers the standardized `URLSearchParams` API and the Node-specific `node:querystring` module. Prefer `URLSearchParams` for new application code shared with browsers and for complete URL handling through `URL`. The querystring module remains stable and can be faster in performance-sensitive parsing, but its data shapes and encoding behavior are not a web-platform standard.

Choose one parser at an application boundary and document its duplicate-key behavior. `URLSearchParams.get()` returns the first value while `getAll()` preserves all values. `querystring.parse()` returns a string or array for repeated keys. Code that assumes every value is one string can mis-handle filters, authorization parameters, or signatures.

Do not split a raw URL on `?` and `&`. Fragments, encoded delimiters, empty values, plus signs, repeated keys, and malformed escapes make manual parsing unreliable. Construct a `URL` with a trusted base for request targets, then read its `searchParams` or pass only the query component to the selected parser.

  • Use URL and URLSearchParams for standardized new code.
  • Use querystring only with a documented Node-specific reason.
  • Define whether duplicates are rejected, first-wins, last-wins, or arrays.
  • Keep URL parsing and query validation as separate steps.

Parse Semantics

`querystring.parse()` accepts the text, pair separator, key-value separator, and options. Its default key limit bounds how many pairs are parsed; choose an explicit application limit before parsing untrusted large input. The returned object has a null prototype, so inherited methods such as `hasOwnProperty` are unavailable and prototype-chain keys are not inherited.

Percent decoding assumes UTF-8 unless a custom decoder is supplied. A plus sign is interpreted according to form-style query rules, while `%2B` represents a literal plus. Test non-ASCII text, empty keys, keys without equals signs, repeated values, and malformed percent sequences against the actual API contract.

Parsing creates strings, not typed business values. Convert booleans, integers, dates, enums, arrays, and pagination cursors with explicit validators. `Number(value)` accepting an empty string or partial date parsing can create surprising defaults; reject invalid representations instead of coercing them silently.

  • Bound pair count and total request-target size.
  • Treat the null-prototype result as a dictionary, not a normal object instance.
  • Test encoding and duplicate-key behavior explicitly.
  • Convert parsed strings through domain validators.

Stringify Semantics

`querystring.stringify()` serializes an object with configurable separators and an optional encoder. Arrays produce repeated keys, while unsupported value types are converted according to the module rules. Build the object from allowlisted application values rather than serializing a large request or database record accidentally.

For `URLSearchParams`, append one pair for each repeated value. Constructing it from a plain object converts an array to one comma-joined string, which is not the same as repeated parameters. An iterable of key-value pairs preserves duplicates and ordering explicitly.

Encoding is not HTML escaping, SQL parameterization, or signature canonicalization. If a third-party API signs query strings, follow its exact sorting, normalization, and percent-encoding specification. Sorting or reserializing `URLSearchParams` can change the visible encoding even when decoded values appear equivalent.

  • Serialize only approved keys and bounded values.
  • Use repeated pairs when the contract allows multiple values.
  • Do not reuse generic query encoding for cryptographic canonicalization.
  • Compare decoded meaning and exact serialized form in tests.

Query Boundary Security

Query parameters are untrusted input even on read-only routes. Attackers can send huge keys, thousands of pairs, expensive regular-expression values, unexpected arrays, conflicting duplicates, and encoded traversal strings. Enforce HTTP server limits, parser limits, per-field length, and a small allowlist before any database or filesystem operation.

Canonicalize once before cache keys, redirects, or authorization decisions. If one layer treats the first duplicate as authoritative and another uses the last, the same URL can pass a check and execute a different value. Reject ambiguous security-sensitive duplicates such as redirect targets, tenant IDs, and signed parameters.

Log validation category and request ID rather than the entire query string, which may contain tokens or personal data. Test invalid encodings, control characters, repeated keys, missing values, unexpected separators, and boundary lengths. A parser success only means the string was decoded; it does not mean the request is safe or meaningful.

  • Limit URL length, pair count, key length, and value length.
  • Reject unknown and ambiguous security-sensitive parameters.
  • Normalize before cache and authorization use.
  • Redact query data from logs and traces.

Repeated Values and Resource Limits

A query can repeat the same key, omit a value, contain an empty value, or use percent-encoded bytes that do not form expected text. Decide whether each application field accepts one value, many values, or no value, then validate the parsed representation. Silently taking the first or last value can turn a harmless parser difference into an authorization or cache inconsistency.

`URLSearchParams` preserves repeated entries and iteration order, while `querystring.parse()` returns an object whose repeated key may become an array and whose object has no ordinary prototype. Neither result is domain data. Convert it into an explicit schema with length, count, numeric range, enum, and normalization rules before using it in database queries or redirects.

Bound the request-target length at the server or proxy and bound the number of keys the parser will accept. Excessive keys, huge values, and expensive downstream searches are resource attacks even when parsing succeeds. Reject malformed or oversized input with a stable client error instead of truncating it invisibly.

Test plus signs, spaces, Unicode, percent signs, repeated keys, blank keys, key-only entries, very large counts, and round trips that are expected to be stable. Do not assume parsing and stringifying reproduce the original byte sequence; canonicalization should be an intentional application contract.

  • Define single-value and multi-value fields explicitly.
  • Validate parser output before business or security decisions.
  • Bound target length, key count, and downstream work.
  • Test encoded, repeated, blank, and malformed inputs.
Before you move on

Query Parameter Compatibility Check

5 checks
  • The Node.js querystring module provides utilities for parsing and formatting URL query strings.
  • Query strings are the key-value pairs that appear after the ? in a URL (e.g., ?name=Alice&age=25).
  • This module allows you to convert query strings into JavaScript objects and vice versa, making it easier to work with URL parameters in web applications.
  • Important Note: The querystring module is considered legacy as of Node.js 10+.
  • For new projects, use the modern URLSearchParams API, which is part of the WHATWG URL standard and provides better functionality with a cleaner API.

Node JS Questions Learners Ask

querystring is a legacy Node-specific API, while URLSearchParams follows the modern web URL standard used across runtimes. URLSearchParams handles repeated keys through getAll(), works naturally with the URL class, and makes browser/server behavior more consistent.

Parsing only converts the query string into keys and string values. It does not prove that page is a positive integer, sort is an allowed field, or limit is within a safe range.

Pick one convention and document it. Repeated keys such as ?tag=node&tag=api map well to URLSearchParams.getAll().

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.