The querystring Module
node:querystring parses and serializes the key=value&key2=value2 strings that live after the ? in a URL or inside a form-encoded request body. It predates the web-standard URLSearchParams — and understanding when to use which is the real lesson of this page.
The two core functions
Function | Direction | Example |
|---|---|---|
| string → object |
|
| object → string |
|
| Percent-encode one component |
|
| Percent-decode one component |
|
import querystring from 'node:querystring'
const parsed = querystring.parse('name=Ada&lang=js&lang=ts')
// → { name: 'Ada', lang: ['js', 'ts'] } repeated key → array
const str = querystring.stringify({ name: 'Ada Lovelace', page: 2 })
// → 'name=Ada%20Lovelace&page=2'Custom separators
Unlike URLSearchParams, querystring lets you change the separator and assignment characters — useful for parsing cookie-like or semicolon-delimited strings:
querystring.parse('a:1;b:2', ';', ':')
// → { a: '1', b: '2' } sep=';' eq=':'
querystring.stringify({ a: 1, b: 2 }, ';', ':')
// → 'a:1;b:2'querystring vs URLSearchParams
This is the decision that matters. The web-standard URLSearchParams (from the url module) is the modern default; querystring survives for legacy code and its custom-separator flexibility:
|
| |
|---|---|---|
Standard | Node-specific (legacy) | WHATWG — works in browsers too |
Returns | A plain object | An iterable object with methods |
Custom separators | Yes ( | No — always |
Space encoding |
|
|
Recommended for new code | No | Yes |
The same task, the modern way
const params = new URLSearchParams('name=Ada&lang=js&lang=ts')
params.get('name') // 'Ada'
params.getAll('lang') // ['js', 'ts']
params.toString() // 'name=Ada&lang=js&lang=ts'A real use: parsing a POST body
Form submissions with Content-Type: application/x-www-form-urlencoded arrive as a query-string body. Here is the bare-Node way to read one (frameworks like Express do this for you):
import { createServer } from 'node:http'
createServer((req, res) => {
let body = ''
req.on('data', (chunk) => { body += chunk })
req.on('end', () => {
const form = new URLSearchParams(body)
res.end(`Hello, ${form.get('name')}`)
})
}).listen(3000)When to use each — the short rule
New code, web URLs, isomorphic (Node + browser) →
URLSearchParams.Need a non-
&/=separator (cookies, custom formats) →querystring.Maintaining old Node code already using it → leave
querystringin place.