query-string vs qs
URL 查询参数解析与序列化方案对比
query-stringqs类似的npm包:

URL 查询参数解析与序列化方案对比

qsquery-string 都是用于处理 URL 查询参数的 JavaScript 工具库,旨在简化参数解析与序列化流程。qs 是一个功能强大的老牌库,擅长处理复杂的嵌套对象和数组,常被 Node.js 生态(如 Express)作为默认依赖。query-string 则更轻量且现代,专注于扁平化参数处理,并提供了解析完整 URL 的便捷方法,适合前端浏览器环境。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
query-string15,462,5146,90757.7 kB26 个月前MIT
qs08,919307 kB691 个月前BSD-3-Clause

qs vs query-string:深度解析与工程选型

在前端开发中,处理 URL 查询参数(Query Parameters)是一个看似简单却暗藏陷阱的任务。qsquery-string 是目前生态中最流行的两个解决方案,它们都能将字符串转换为对象,或将对象序列化为字符串,但设计哲学和默认行为有很大不同。本文将从实际工程场景出发,对比两者的核心差异。

🧱 嵌套对象支持:深层结构 vs 扁平化处理

qs 天生支持深层嵌套对象,能够准确还原复杂的数据结构。

  • 适合处理类似表单提交后的复杂数据。
  • 默认将嵌套对象解析为多层 JSON 结构。
import qs from 'qs';

const query = 'user[name]=Alice&user[age]=30';
const parsed = qs.parse(query);
// 结果:{ user: { name: 'Alice', age: '30' } }

const stringified = qs.stringify({ user: { name: 'Bob' } });
// 结果:'user%5Bname%5D=Bob'

query-string 默认将键名视为字符串,不自动解析嵌套结构。

  • 适合处理扁平化的参数列表。
  • 嵌套键名会保留为字符串形式,需手动处理。
import queryString from 'query-string';

const query = 'user[name]=Alice&user[age]=30';
const parsed = queryString.parse(query);
// 结果:{ 'user[name]': 'Alice', 'user[age]': '30' }

const stringified = queryString.stringify({ user: { name: 'Bob' } });
// 结果:'user%5Bname%5D=Bob' (对象会被转为字符串 '[object Object]' 或需手动处理)

📦 数组序列化:带索引 vs 重复键名

数组的处理方式是两者最大的分歧点,直接影响后端接收数据的格式。

qs 默认在数组中添加索引。

  • 生成格式:foo[0]=1&foo[1]=2
  • 优点:保留顺序,兼容 PHP 等传统后端。
  • 缺点:URL 较长,某些现代框架偏好无索引格式。
import qs from 'qs';

const data = { ids: [1, 2, 3] };
const result = qs.stringify(data);
// 结果:'ids[0]=1&ids[1]=2&ids[2]=3'

// 可通过配置关闭索引
const noIndices = qs.stringify(data, { indices: false });
// 结果:'ids[]=1&ids[]=2&ids[]=3'

query-string 默认使用重复键名。

  • 生成格式:foo=1&foo=2&foo=3
  • 优点:URL 更简洁,符合 RESTful 常见实践。
  • 缺点:某些后端可能需要特定配置才能识别为数组。
import queryString from 'query-string';

const data = { ids: [1, 2, 3] };
const result = queryString.stringify(data);
// 结果:'ids=1&ids=2&ids=3'

🌐 URL 解析能力:仅参数 vs 完整 URL

处理完整 URL 而不仅仅是查询字符串时,两者的便利性差异明显。

qs 专注于查询字符串本身。

  • 需要手动提取 window.location.search 或使用 URL 对象。
  • 不包含解析路径或哈希的功能。
import qs from 'qs';

const url = 'https://example.com/path?foo=1#hash';
const queryPart = new URL(url).search.slice(1);
const parsed = qs.parse(queryPart);
// 结果:{ foo: '1' }
// 需要额外步骤提取查询部分

query-string 提供 parseUrl 方法。

  • 一次性解析路径、参数和哈希。
  • 前端路由处理更加便捷。
import queryString from 'query-string';

const url = 'https://example.com/path?foo=1#hash';
const parsed = queryString.parseUrl(url);
// 结果:{ url: 'https://example.com/path', query: { foo: '1' }, hash: 'hash' }

🔒 编码与安全:严格模式 vs 标准兼容

URL 编码的处理直接影响特殊字符的传输安全。

qs 提供严格的编码控制。

  • 默认启用严格模式,避免潜在的安全问题。
  • 可自定义编码器以适应特殊需求。
import qs from 'qs';

const data = { key: 'a+b' };
// 默认严格编码,加号会被转义
const result = qs.stringify(data);
// 结果:'key=a%2Bb'

query-string 遵循现代 URL 标准。

  • 默认行为更贴近浏览器原生 URLSearchParams
  • 对特殊字符的处理更加直观。
import queryString from 'query-string';

const data = { key: 'a+b' };
const result = queryString.stringify(data);
// 结果:'key=a%2Bb' (行为与 qs 类似,但配置项更少)

🤝 共同点:生态中的稳定基石

尽管存在差异,这两个库在核心目标上是一致的,都是为了解决原生 API 的不足。

1. 🔄 双向转换能力

  • 都支持 parse(字符串转对象)和 stringify(对象转字符串)。
  • 解决了原生 URLSearchParams 在处理嵌套对象时的局限性。
// qs 双向转换
const obj = qs.parse('a=1');
const str = qs.stringify(obj);

// query-string 双向转换
const obj = queryString.parse('a=1');
const str = queryString.stringify(obj);

2. 🛠️ 配置灵活性

  • 都允许通过配置项调整行为(如数组格式、编码方式)。
  • 适应不同后端框架的偏好。
// qs 配置数组格式
qs.stringify({ a: [1] }, { arrayFormat: 'bracket' });

// query-string 配置数组格式
queryString.stringify({ a: [1] }, { arrayFormat: 'bracket' });

3. ✅ 广泛的生产环境验证

  • 都被大量知名项目使用,稳定性极高。
  • 社区活跃,问题修复及时。
// 两者都支持 TypeScript 类型定义
import qs from 'qs'; // @types/qs
import queryString from 'query-string'; // 内置类型

📊 核心差异总结

特性qsquery-string
嵌套对象✅ 自动解析为多层对象❌ 默认保留为扁平键名
数组格式🔢 默认带索引 ([0])🔁 默认重复键名 (key=val&key=val)
URL 解析❌ 仅处理查询字符串✅ 支持 parseUrl 解析完整 URL
默认依赖🟢 Express 默认内置⚪ 需单独安装
配置复杂度🎚️ 高(众多选项)🎛️ 低(简洁直观)

💡 最终建议

qs 就像一把多功能瑞士军刀 🇨🇭 — 适合需要处理复杂数据结构、与 Node.js 后端深度集成的场景。如果你的后端是 Express 或需要精确控制数组索引格式,它是首选。

query-string 更像是一把轻便的折叠刀 🔪 — 适合现代前端应用,尤其是单页应用(SPA)中处理路由参数。如果你需要解析完整 URL 或偏好简洁的重复键名格式,它能让你少写很多样板代码。

核心原则:不要混用。在一个项目中,确保前后端使用相同的库或配置,否则数组和嵌套对象的结构差异会导致难以排查的数据丢失问题。

如何选择: query-string vs qs

  • query-string:

    如果你主要在浏览器端处理简单的扁平参数,或者需要频繁解析完整 URL(包括路径和哈希),query-string 是更好的选择。它的 API 更简洁,默认行为符合现代 URL 标准,且没有复杂的嵌套逻辑,代码更易维护。对于大多数单页应用(SPA)的路由参数管理,它提供的 parseUrl 等 helper 方法能显著减少样板代码。

  • qs:

    如果你的项目需要处理深层嵌套的对象结构,或者后端服务(如 Express)默认使用 qs 格式,那么选择 qs 是最稳妥的方案。它提供了丰富的配置选项来控制数组索引和编码行为,适合对数据格式有严格要求的场景。此外,当你需要确保前后端参数序列化的完全一致性,尤其是涉及复杂表单提交时,qs 的严格模式能减少很多潜在的错误。

query-string的README

query-string

Parse and stringify URL query strings

Install

npm install query-string

[!WARNING] Remember the hyphen! Do not install the deprecated querystring package!

For browser usage, this package targets the latest version of Chrome, Firefox, and Safari.

Usage

import queryString from 'query-string';

console.log(location.search);
//=> '?foo=bar'

const parsed = queryString.parse(location.search);
console.log(parsed);
//=> {foo: 'bar'}

console.log(location.hash);
//=> '#token=bada55cafe'

const parsedHash = queryString.parse(location.hash);
console.log(parsedHash);
//=> {token: 'bada55cafe'}

parsed.foo = 'unicorn';
parsed.ilike = 'pizza';

const stringified = queryString.stringify(parsed);
//=> 'foo=unicorn&ilike=pizza'

location.search = stringified;
// note that `location.search` automatically prepends a question mark
console.log(location.search);
//=> '?foo=unicorn&ilike=pizza'

API

.parse(string, options?)

Parse a query string into an object. Leading ? or # are ignored, so you can pass location.search or location.hash directly.

The returned object is created with Object.create(null) and thus does not have a prototype.

queryString.parse('?foo=bar');
//=> {foo: 'bar'}

queryString.parse('#token=secret&name=jhon');
//=> {token: 'secret', name: 'jhon'}

options

Type: object

decode

Type: boolean
Default: true

Decode the keys and values. URL components are decoded with decode-uri-component.

arrayFormat

Type: string
Default: 'none'

  • 'bracket': Parse arrays with bracket representation:
import queryString from 'query-string';

queryString.parse('foo[]=1&foo[]=2&foo[]=3', {arrayFormat: 'bracket'});
//=> {foo: ['1', '2', '3']}
  • 'index': Parse arrays with index representation:
import queryString from 'query-string';

queryString.parse('foo[0]=1&foo[1]=2&foo[3]=3', {arrayFormat: 'index'});
//=> {foo: ['1', '2', '3']}
  • 'comma': Parse arrays with elements separated by comma:
import queryString from 'query-string';

queryString.parse('foo=1,2,3', {arrayFormat: 'comma'});
//=> {foo: ['1', '2', '3']}
  • 'separator': Parse arrays with elements separated by a custom character:
import queryString from 'query-string';

queryString.parse('foo=1|2|3', {arrayFormat: 'separator', arrayFormatSeparator: '|'});
//=> {foo: ['1', '2', '3']}
  • 'bracket-separator': Parse arrays (that are explicitly marked with brackets) with elements separated by a custom character:
import queryString from 'query-string';

queryString.parse('foo[]', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> {foo: []}

queryString.parse('foo[]=', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> {foo: ['']}

queryString.parse('foo[]=1', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> {foo: ['1']}

queryString.parse('foo[]=1|2|3', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> {foo: ['1', '2', '3']}

queryString.parse('foo[]=1||3|||6', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> {foo: ['1', '', 3, '', '', '6']}

queryString.parse('foo[]=1|2|3&bar=fluffy&baz[]=4', {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> {foo: ['1', '2', '3'], bar: 'fluffy', baz:['4']}
  • 'colon-list-separator': Parse arrays with parameter names that are explicitly marked with :list:
import queryString from 'query-string';

queryString.parse('foo:list=one&foo:list=two', {arrayFormat: 'colon-list-separator'});
//=> {foo: ['one', 'two']}
  • 'none': Parse arrays with elements using duplicate keys:
import queryString from 'query-string';

queryString.parse('foo=1&foo=2&foo=3');
//=> {foo: ['1', '2', '3']}
arrayFormatSeparator

Type: string
Default: ','

The character used to separate array elements when using {arrayFormat: 'separator'}.

sort

Type: Function | boolean
Default: true

Supports both Function as a custom sorting function or false to disable sorting.

parseNumbers

Type: boolean
Default: false

import queryString from 'query-string';

queryString.parse('foo=1', {parseNumbers: true});
//=> {foo: 1}

Parse the value as a number type instead of string type if it's a number.

parseBooleans

Type: boolean
Default: false

import queryString from 'query-string';

queryString.parse('foo=true', {parseBooleans: true});
//=> {foo: true}

Parse the value as a boolean type instead of string type if it's a boolean.

types

Type: object
Default: {}

Specifies a schema for parsing query values with explicit type declarations. When defined, the types provided here take precedence over general parsing options such as parseNumbers, parseBooleans, and arrayFormat.

Use this option to explicitly define the type of a specific parameter—particularly useful in cases where the type might otherwise be ambiguous (e.g., phone numbers or IDs).

You can also provide a custom function to transform the value. The function will receive the raw string and should return the desired parsed result. When used with array formats (like comma, separator, bracket, etc.), the function is applied to each array element individually.

Supported Types:

  • 'boolean': Parse flagged as a boolean (overriding the parseBooleans option):
queryString.parse('?isAdmin=true&flagged=true&isOkay=0', {
		parseBooleans: false,
		types: {
				flagged: 'boolean',
				isOkay: 'boolean',
		},
});
//=> {isAdmin: 'true', flagged: true, isOkay: false}

Note: The 'boolean' type also converts '0' and '1' to booleans, and treats valueless keys (e.g. ?flag) as true.

  • 'string': Parse phoneNumber as a string (overriding the parseNumbers option):
import queryString from 'query-string';

queryString.parse('?phoneNumber=%2B380951234567&id=1', {
	parseNumbers: true,
	types: {
		phoneNumber: 'string',
	}
});
//=> {phoneNumber: '+380951234567', id: 1}
  • 'number': Parse age as a number (even when parseNumbers is false):
import queryString from 'query-string';

queryString.parse('?age=20&id=01234&zipcode=90210', {
	types: {
		age: 'number',
	}
});
//=> {age: 20, id: '01234', zipcode: '90210'}
  • 'string[]': Parse items as an array of strings (overriding the parseNumbers option):
import queryString from 'query-string';

queryString.parse('?age=20&items=1%2C2%2C3', {
	parseNumbers: true,
	types: {
		items: 'string[]',
	}
});
//=> {age: 20, items: ['1', '2', '3']}
  • 'number[]': Parse items as an array of numbers (even when parseNumbers is false):
import queryString from 'query-string';

queryString.parse('?age=20&items=1%2C2%2C3', {
	types: {
		items: 'number[]',
	}
});
//=> {age: '20', items: [1, 2, 3]}
  • 'Function': Provide a custom function as the parameter type. The parameter's value will equal the function's return value. When used with array formats (like comma, separator, bracket, etc.), the function is applied to each array element individually.
import queryString from 'query-string';

queryString.parse('?age=20&id=01234&zipcode=90210', {
	types: {
		age: value => value * 2,
	}
});
//=> {age: 40, id: '01234', zipcode: '90210'}

// With arrays, the function is applied to each element
queryString.parse('?scores=10,20,30', {
	arrayFormat: 'comma',
	types: {
		scores: value => Number(value) * 2,
	}
});
//=> {scores: [20, 40, 60]}

NOTE: Array types (string[], number[]) are ignored if arrayFormat is set to 'none'.

queryString.parse('ids=001%2C002%2C003&foods=apple%2Corange%2Cmango', {
	arrayFormat: 'none',
	types: {
		ids: 'number[]',
		foods: 'string[]',
	},
}
//=> {ids:'001,002,003', foods:'apple,orange,mango'}
Function
import queryString from 'query-string';

queryString.parse('?age=20&id=01234&zipcode=90210', {
	types: {
		age: value => value * 2,
	}
});
//=> {age: 40, id: '01234', zipcode: '90210'}

Parse the value as a boolean type instead of string type if it's a boolean.

.stringify(object, options?)

Stringify an object into a query string and sorting the keys.

Supported value types: string, number, bigint, boolean, null, undefined, and arrays of these types. Other types like Symbol, functions, or objects (except arrays) will throw an error.

options

Type: object

strict

Type: boolean
Default: true

Strictly encode URI components. It uses encodeURIComponent if set to false. You probably don't care about this option.

encode

Type: boolean
Default: true

URL encode the keys and values.

arrayFormat

Type: string
Default: 'none'

  • 'bracket': Serialize arrays using bracket representation:
import queryString from 'query-string';

queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'bracket'});
//=> 'foo[]=1&foo[]=2&foo[]=3'
  • 'index': Serialize arrays using index representation:
import queryString from 'query-string';

queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'index'});
//=> 'foo[0]=1&foo[1]=2&foo[2]=3'
  • 'comma': Serialize arrays by separating elements with comma:
import queryString from 'query-string';

queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'comma'});
//=> 'foo=1,2,3'

queryString.stringify({foo: [1, null, '']}, {arrayFormat: 'comma'});
//=> 'foo=1,,'
// Note that typing information for null values is lost
// and `.parse('foo=1,,')` would return `{foo: [1, '', '']}`.
  • 'separator': Serialize arrays by separating elements with a custom character:
import queryString from 'query-string';

queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'separator', arrayFormatSeparator: '|'});
//=> 'foo=1|2|3'
  • 'bracket-separator': Serialize arrays by explicitly post-fixing array names with brackets and separating elements with a custom character:
import queryString from 'query-string';

queryString.stringify({foo: []}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> 'foo[]'

queryString.stringify({foo: ['']}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> 'foo[]='

queryString.stringify({foo: [1]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> 'foo[]=1'

queryString.stringify({foo: [1, 2, 3]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> 'foo[]=1|2|3'

queryString.stringify({foo: [1, '', 3, null, null, 6]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> 'foo[]=1||3|||6'

queryString.stringify({foo: [1, '', 3, null, null, 6]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|', skipNull: true});
//=> 'foo[]=1||3|6'

queryString.stringify({foo: [1, 2, 3], bar: 'fluffy', baz: [4]}, {arrayFormat: 'bracket-separator', arrayFormatSeparator: '|'});
//=> 'foo[]=1|2|3&bar=fluffy&baz[]=4'
  • 'colon-list-separator': Serialize arrays with parameter names that are explicitly marked with :list:
import queryString from 'query-string';

queryString.stringify({foo: ['one', 'two']}, {arrayFormat: 'colon-list-separator'});
//=> 'foo:list=one&foo:list=two'
  • 'none': Serialize arrays by using duplicate keys:
import queryString from 'query-string';

queryString.stringify({foo: [1, 2, 3]});
//=> 'foo=1&foo=2&foo=3'
arrayFormatSeparator

Type: string
Default: ','

The character used to separate array elements when using {arrayFormat: 'separator'}.

sort

Type: Function | boolean

Supports both Function as a custom sorting function or false to disable sorting.

import queryString from 'query-string';

const order = ['c', 'a', 'b'];

queryString.stringify({a: 1, b: 2, c: 3}, {
	sort: (a, b) => order.indexOf(a) - order.indexOf(b)
});
//=> 'c=3&a=1&b=2'
import queryString from 'query-string';

queryString.stringify({b: 1, c: 2, a: 3}, {sort: false});
//=> 'b=1&c=2&a=3'

If omitted, keys are sorted using Array#sort(), which means, converting them to strings and comparing strings in Unicode code point order.

skipNull

Skip keys with null as the value.

Note that keys with undefined as the value are always skipped.

Type: boolean
Default: false

import queryString from 'query-string';

queryString.stringify({a: 1, b: undefined, c: null, d: 4}, {
	skipNull: true
});
//=> 'a=1&d=4'
import queryString from 'query-string';

queryString.stringify({a: undefined, b: null}, {
	skipNull: true
});
//=> ''
skipEmptyString

Skip keys with an empty string as the value.

Type: boolean
Default: false

import queryString from 'query-string';

queryString.stringify({a: 1, b: '', c: '', d: 4}, {
	skipEmptyString: true
});
//=> 'a=1&d=4'
import queryString from 'query-string';

queryString.stringify({a: '', b: ''}, {
	skipEmptyString: true
});
//=> ''
replacer

A function that transforms key-value pairs before stringification.

Type: function
Default: undefined

Similar to the replacer parameter of JSON.stringify(), this function is called for each key-value pair and can be used to transform values before they are stringified. The function receives the key and value, and should return the transformed value. Returning undefined will omit the key-value pair from the resulting query string.

This is useful for custom serialization of non-primitive types like Date:

import queryString from 'query-string';

queryString.stringify({
	date: new Date('2024-01-15T10:30:00Z'),
	name: 'John'
}, {
	replacer: (key, value) => {
		if (value instanceof Date) {
			return value.toISOString();
		}

		return value;
	}
});
//=> 'date=2024-01-15T10%3A30%3A00.000Z&name=John'

You can also use it to filter out keys:

import queryString from 'query-string';

queryString.stringify({
	a: 1,
	b: null,
	c: 3
}, {
	replacer: (key, value) => value === null ? undefined : value
});
//=> 'a=1&c=3'

.extract(string)

Extract a query string from a URL that can be passed into .parse().

queryString.extract('https://foo.bar?foo=bar');
//=> 'foo=bar'

.parseUrl(string, options?)

Extract the URL and the query string as an object.

Returns an object with a url and query property.

If the parseFragmentIdentifier option is true, the object will also contain a fragmentIdentifier property.

import queryString from 'query-string';

queryString.parseUrl('https://foo.bar?foo=bar');
//=> {url: 'https://foo.bar', query: {foo: 'bar'}}

queryString.parseUrl('https://foo.bar?foo=bar#xyz', {parseFragmentIdentifier: true});
//=> {url: 'https://foo.bar', query: {foo: 'bar'}, fragmentIdentifier: 'xyz'}

options

Type: object

The options are the same as for .parse().

Extra options are as below.

parseFragmentIdentifier

Parse the fragment identifier from the URL.

Type: boolean
Default: false

import queryString from 'query-string';

queryString.parseUrl('https://foo.bar?foo=bar#xyz', {parseFragmentIdentifier: true});
//=> {url: 'https://foo.bar', query: {foo: 'bar'}, fragmentIdentifier: 'xyz'}

.stringifyUrl(object, options?)

Stringify an object into a URL with a query string and sorting the keys. The inverse of .parseUrl()

The options are the same as for .stringify().

Returns a string with the URL and a query string.

Query items in the query property overrides queries in the url property.

The fragmentIdentifier property overrides the fragment identifier in the url property.

queryString.stringifyUrl({url: 'https://foo.bar', query: {foo: 'bar'}});
//=> 'https://foo.bar?foo=bar'

queryString.stringifyUrl({url: 'https://foo.bar?foo=baz', query: {foo: 'bar'}});
//=> 'https://foo.bar?foo=bar'

queryString.stringifyUrl({
	url: 'https://foo.bar',
	query: {
		top: 'foo'
	},
	fragmentIdentifier: 'bar'
});
//=> 'https://foo.bar?top=foo#bar'

object

Type: object

url

Type: string

The URL to stringify.

query

Type: object

Query items to add to the URL.

.pick(url, keys, options?)

.pick(url, filter, options?)

Pick query parameters from a URL.

Returns a string with the new URL.

import queryString from 'query-string';

queryString.pick('https://foo.bar?foo=1&bar=2#hello', ['foo']);
//=> 'https://foo.bar?foo=1#hello'

queryString.pick('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
//=> 'https://foo.bar?bar=2#hello'

.exclude(url, keys, options?)

.exclude(url, filter, options?)

Exclude query parameters from a URL.

Returns a string with the new URL.

import queryString from 'query-string';

queryString.exclude('https://foo.bar?foo=1&bar=2#hello', ['foo']);
//=> 'https://foo.bar?bar=2#hello'

queryString.exclude('https://foo.bar?foo=1&bar=2#hello', (name, value) => value === 2, {parseNumbers: true});
//=> 'https://foo.bar?foo=1#hello'

url

Type: string

The URL containing the query parameters to filter.

keys

Type: string[]

The names of the query parameters to filter based on the function used.

filter

Type: (key, value) => boolean

A filter predicate that will be provided the name of each query parameter and its value. The parseNumbers and parseBooleans options also affect value.

options

Type: object

Parse options and stringify options.

Nesting

This module intentionally doesn't support nesting as it's not spec'd and varies between implementations, which causes a lot of edge cases.

You're much better off just converting the object to a JSON string:

import queryString from 'query-string';

queryString.stringify({
	foo: 'bar',
	nested: JSON.stringify({
		unicorn: 'cake'
	})
});
//=> 'foo=bar&nested=%7B%22unicorn%22%3A%22cake%22%7D'

However, there is support for multiple instances of the same key:

import queryString from 'query-string';

queryString.parse('likes=cake&name=bob&likes=icecream');
//=> {likes: ['cake', 'icecream'], name: 'bob'}

queryString.stringify({color: ['taupe', 'chartreuse'], id: '515'});
//=> 'color=taupe&color=chartreuse&id=515'

Falsy values

Sometimes you want to unset a key, or maybe just make it present without assigning a value to it. Here is how falsy values are stringified:

import queryString from 'query-string';

queryString.stringify({foo: false});
//=> 'foo=false'

queryString.stringify({foo: null});
//=> 'foo'

queryString.stringify({foo: undefined});
//=> ''

FAQ

Why is it parsing + as a space?

See this answer.