UNPKG

21 kBMarkdownView Raw
1# qs <sup>[![Version Badge][npm-version-svg]][package-url]</sup>
2
3[![github actions][actions-image]][actions-url]
4[![coverage][codecov-image]][codecov-url]
5[![dependency status][deps-svg]][deps-url]
6[![dev dependency status][dev-deps-svg]][dev-deps-url]
7[![License][license-image]][license-url]
8[![Downloads][downloads-image]][downloads-url]
9
10[![npm badge][npm-badge-png]][package-url]
11
12A querystring parsing and stringifying library with some added security.
13
14Lead Maintainer: [Jordan Harband](https://github.com/ljharb)
15
16The **qs** module was originally created and maintained by [TJ Holowaychuk](https://github.com/visionmedia/node-querystring).
17
18## Usage
19
20```javascript
21var qs = require('qs');
22var assert = require('assert');
23
24var obj = qs.parse('a=c');
25assert.deepEqual(obj, { a: 'c' });
26
27var str = qs.stringify(obj);
28assert.equal(str, 'a=c');
29```
30
31### Parsing Objects
32
33[](#preventEval)
34```javascript
35qs.parse(string, [options]);
36```
37
38**qs** allows you to create nested objects within your query strings, by surrounding the name of sub-keys with square brackets `[]`.
39For example, the string `'foo[bar]=baz'` converts to:
40
41```javascript
42assert.deepEqual(qs.parse('foo[bar]=baz'), {
43 foo: {
44 bar: 'baz'
45 }
46});
47```
48
49When using the `plainObjects` option the parsed value is returned as a null object, created via `Object.create(null)` and as such you should be aware that prototype methods will not exist on it and a user may set those names to whatever value they like:
50
51```javascript
52var nullObject = qs.parse('a[hasOwnProperty]=b', { plainObjects: true });
53assert.deepEqual(nullObject, { a: { hasOwnProperty: 'b' } });
54```
55
56By default parameters that would overwrite properties on the object prototype are ignored, if you wish to keep the data from those fields either use `plainObjects` as mentioned above, or set `allowPrototypes` to `true` which will allow user input to overwrite those properties. *WARNING* It is generally a bad idea to enable this option as it can cause problems when attempting to use the properties that have been overwritten. Always be careful with this option.
57
58```javascript
59var protoObject = qs.parse('a[hasOwnProperty]=b', { allowPrototypes: true });
60assert.deepEqual(protoObject, { a: { hasOwnProperty: 'b' } });
61```
62
63URI encoded strings work too:
64
65```javascript
66assert.deepEqual(qs.parse('a%5Bb%5D=c'), {
67 a: { b: 'c' }
68});
69```
70
71You can also nest your objects, like `'foo[bar][baz]=foobarbaz'`:
72
73```javascript
74assert.deepEqual(qs.parse('foo[bar][baz]=foobarbaz'), {
75 foo: {
76 bar: {
77 baz: 'foobarbaz'
78 }
79 }
80});
81```
82
83By default, when nesting objects **qs** will only parse up to 5 children deep. This means if you attempt to parse a string like
84`'a[b][c][d][e][f][g][h][i]=j'` your resulting object will be:
85
86```javascript
87var expected = {
88 a: {
89 b: {
90 c: {
91 d: {
92 e: {
93 f: {
94 '[g][h][i]': 'j'
95 }
96 }
97 }
98 }
99 }
100 }
101};
102var string = 'a[b][c][d][e][f][g][h][i]=j';
103assert.deepEqual(qs.parse(string), expected);
104```
105
106This depth can be overridden by passing a `depth` option to `qs.parse(string, [options])`:
107
108```javascript
109var deep = qs.parse('a[b][c][d][e][f][g][h][i]=j', { depth: 1 });
110assert.deepEqual(deep, { a: { b: { '[c][d][e][f][g][h][i]': 'j' } } });
111```
112
113The depth limit helps mitigate abuse when **qs** is used to parse user input, and it is recommended to keep it a reasonably small number.
114
115For similar reasons, by default **qs** will only parse up to 1000 parameters. This can be overridden by passing a `parameterLimit` option:
116
117```javascript
118var limited = qs.parse('a=b&c=d', { parameterLimit: 1 });
119assert.deepEqual(limited, { a: 'b' });
120```
121
122To bypass the leading question mark, use `ignoreQueryPrefix`:
123
124```javascript
125var prefixed = qs.parse('?a=b&c=d', { ignoreQueryPrefix: true });
126assert.deepEqual(prefixed, { a: 'b', c: 'd' });
127```
128
129An optional delimiter can also be passed:
130
131```javascript
132var delimited = qs.parse('a=b;c=d', { delimiter: ';' });
133assert.deepEqual(delimited, { a: 'b', c: 'd' });
134```
135
136Delimiters can be a regular expression too:
137
138```javascript
139var regexed = qs.parse('a=b;c=d,e=f', { delimiter: /[;,]/ });
140assert.deepEqual(regexed, { a: 'b', c: 'd', e: 'f' });
141```
142
143Option `allowDots` can be used to enable dot notation:
144
145```javascript
146var withDots = qs.parse('a.b=c', { allowDots: true });
147assert.deepEqual(withDots, { a: { b: 'c' } });
148```
149
150If you have to deal with legacy browsers or services, there's
151also support for decoding percent-encoded octets as iso-8859-1:
152
153```javascript
154var oldCharset = qs.parse('a=%A7', { charset: 'iso-8859-1' });
155assert.deepEqual(oldCharset, { a: '§' });
156```
157
158Some services add an initial `utf8=✓` value to forms so that old
159Internet Explorer versions are more likely to submit the form as
160utf-8. Additionally, the server can check the value against wrong
161encodings of the checkmark character and detect that a query string
162or `application/x-www-form-urlencoded` body was *not* sent as
163utf-8, eg. if the form had an `accept-charset` parameter or the
164containing page had a different character set.
165
166**qs** supports this mechanism via the `charsetSentinel` option.
167If specified, the `utf8` parameter will be omitted from the
168returned object. It will be used to switch to `iso-8859-1`/`utf-8`
169mode depending on how the checkmark is encoded.
170
171**Important**: When you specify both the `charset` option and the
172`charsetSentinel` option, the `charset` will be overridden when
173the request contains a `utf8` parameter from which the actual
174charset can be deduced. In that sense the `charset` will behave
175as the default charset rather than the authoritative charset.
176
177```javascript
178var detectedAsUtf8 = qs.parse('utf8=%E2%9C%93&a=%C3%B8', {
179 charset: 'iso-8859-1',
180 charsetSentinel: true
181});
182assert.deepEqual(detectedAsUtf8, { a: 'ø' });
183
184// Browsers encode the checkmark as &#10003; when submitting as iso-8859-1:
185var detectedAsIso8859_1 = qs.parse('utf8=%26%2310003%3B&a=%F8', {
186 charset: 'utf-8',
187 charsetSentinel: true
188});
189assert.deepEqual(detectedAsIso8859_1, { a: 'ø' });
190```
191
192If you want to decode the `&#...;` syntax to the actual character,
193you can specify the `interpretNumericEntities` option as well:
194
195```javascript
196var detectedAsIso8859_1 = qs.parse('a=%26%239786%3B', {
197 charset: 'iso-8859-1',
198 interpretNumericEntities: true
199});
200assert.deepEqual(detectedAsIso8859_1, { a: '☺' });
201```
202
203It also works when the charset has been detected in `charsetSentinel`
204mode.
205
206### Parsing Arrays
207
208**qs** can also parse arrays using a similar `[]` notation:
209
210```javascript
211var withArray = qs.parse('a[]=b&a[]=c');
212assert.deepEqual(withArray, { a: ['b', 'c'] });
213```
214
215You may specify an index as well:
216
217```javascript
218var withIndexes = qs.parse('a[1]=c&a[0]=b');
219assert.deepEqual(withIndexes, { a: ['b', 'c'] });
220```
221
222Note that the only difference between an index in an array and a key in an object is that the value between the brackets must be a number
223to create an array. When creating arrays with specific indices, **qs** will compact a sparse array to only the existing values preserving
224their order:
225
226```javascript
227var noSparse = qs.parse('a[1]=b&a[15]=c');
228assert.deepEqual(noSparse, { a: ['b', 'c'] });
229```
230
231You may also use `allowSparse` option to parse sparse arrays:
232
233```javascript
234var sparseArray = qs.parse('a[1]=2&a[3]=5', { allowSparse: true });
235assert.deepEqual(sparseArray, { a: [, '2', , '5'] });
236```
237
238Note that an empty string is also a value, and will be preserved:
239
240```javascript
241var withEmptyString = qs.parse('a[]=&a[]=b');
242assert.deepEqual(withEmptyString, { a: ['', 'b'] });
243
244var withIndexedEmptyString = qs.parse('a[0]=b&a[1]=&a[2]=c');
245assert.deepEqual(withIndexedEmptyString, { a: ['b', '', 'c'] });
246```
247
248**qs** will also limit specifying indices in an array to a maximum index of `20`. Any array members with an index of greater than `20` will
249instead be converted to an object with the index as the key. This is needed to handle cases when someone sent, for example, `a[999999999]` and it will take significant time to iterate over this huge array.
250
251```javascript
252var withMaxIndex = qs.parse('a[100]=b');
253assert.deepEqual(withMaxIndex, { a: { '100': 'b' } });
254```
255
256This limit can be overridden by passing an `arrayLimit` option:
257
258```javascript
259var withArrayLimit = qs.parse('a[1]=b', { arrayLimit: 0 });
260assert.deepEqual(withArrayLimit, { a: { '1': 'b' } });
261```
262
263To disable array parsing entirely, set `parseArrays` to `false`.
264
265```javascript
266var noParsingArrays = qs.parse('a[]=b', { parseArrays: false });
267assert.deepEqual(noParsingArrays, { a: { '0': 'b' } });
268```
269
270If you mix notations, **qs** will merge the two items into an object:
271
272```javascript
273var mixedNotation = qs.parse('a[0]=b&a[b]=c');
274assert.deepEqual(mixedNotation, { a: { '0': 'b', b: 'c' } });
275```
276
277You can also create arrays of objects:
278
279```javascript
280var arraysOfObjects = qs.parse('a[][b]=c');
281assert.deepEqual(arraysOfObjects, { a: [{ b: 'c' }] });
282```
283
284Some people use comma to join array, **qs** can parse it:
285```javascript
286var arraysOfObjects = qs.parse('a=b,c', { comma: true })
287assert.deepEqual(arraysOfObjects, { a: ['b', 'c'] })
288```
289(_this cannot convert nested objects, such as `a={b:1},{c:d}`_)
290
291### Parsing primitive/scalar values (numbers, booleans, null, etc)
292
293By default, all values are parsed as strings. This behavior will not change and is explained in [issue #91](https://github.com/ljharb/qs/issues/91).
294
295```javascript
296var primitiveValues = qs.parse('a=15&b=true&c=null');
297assert.deepEqual(primitiveValues, { a: '15', b: 'true', c: 'null' });
298```
299
300If you wish to auto-convert values which look like numbers, booleans, and other values into their primitive counterparts, you can use the [query-types Express JS middleware](https://github.com/xpepermint/query-types) which will auto-convert all request query parameters.
301
302### Stringifying
303
304[](#preventEval)
305```javascript
306qs.stringify(object, [options]);
307```
308
309When stringifying, **qs** by default URI encodes output. Objects are stringified as you would expect:
310
311```javascript
312assert.equal(qs.stringify({ a: 'b' }), 'a=b');
313assert.equal(qs.stringify({ a: { b: 'c' } }), 'a%5Bb%5D=c');
314```
315
316This encoding can be disabled by setting the `encode` option to `false`:
317
318```javascript
319var unencoded = qs.stringify({ a: { b: 'c' } }, { encode: false });
320assert.equal(unencoded, 'a[b]=c');
321```
322
323Encoding can be disabled for keys by setting the `encodeValuesOnly` option to `true`:
324```javascript
325var encodedValues = qs.stringify(
326 { a: 'b', c: ['d', 'e=f'], f: [['g'], ['h']] },
327 { encodeValuesOnly: true }
328);
329assert.equal(encodedValues,'a=b&c[0]=d&c[1]=e%3Df&f[0][0]=g&f[1][0]=h');
330```
331
332This encoding can also be replaced by a custom encoding method set as `encoder` option:
333
334```javascript
335var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str) {
336 // Passed in values `a`, `b`, `c`
337 return // Return encoded string
338}})
339```
340
341_(Note: the `encoder` option does not apply if `encode` is `false`)_
342
343Analogue to the `encoder` there is a `decoder` option for `parse` to override decoding of properties and values:
344
345```javascript
346var decoded = qs.parse('x=z', { decoder: function (str) {
347 // Passed in values `x`, `z`
348 return // Return decoded string
349}})
350```
351
352You can encode keys and values using different logic by using the type argument provided to the encoder:
353
354```javascript
355var encoded = qs.stringify({ a: { b: 'c' } }, { encoder: function (str, defaultEncoder, charset, type) {
356 if (type === 'key') {
357 return // Encoded key
358 } else if (type === 'value') {
359 return // Encoded value
360 }
361}})
362```
363
364The type argument is also provided to the decoder:
365
366```javascript
367var decoded = qs.parse('x=z', { decoder: function (str, defaultDecoder, charset, type) {
368 if (type === 'key') {
369 return // Decoded key
370 } else if (type === 'value') {
371 return // Decoded value
372 }
373}})
374```
375
376Examples beyond this point will be shown as though the output is not URI encoded for clarity. Please note that the return values in these cases *will* be URI encoded during real usage.
377
378When arrays are stringified, by default they are given explicit indices:
379
380```javascript
381qs.stringify({ a: ['b', 'c', 'd'] });
382// 'a[0]=b&a[1]=c&a[2]=d'
383```
384
385You may override this by setting the `indices` option to `false`:
386
387```javascript
388qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false });
389// 'a=b&a=c&a=d'
390```
391
392You may use the `arrayFormat` option to specify the format of the output array:
393
394```javascript
395qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })
396// 'a[0]=b&a[1]=c'
397qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })
398// 'a[]=b&a[]=c'
399qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })
400// 'a=b&a=c'
401qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'comma' })
402// 'a=b,c'
403```
404
405Note: when using `arrayFormat` set to `'comma'`, you can also pass the `commaRoundTrip` option set to `true` or `false`, to append `[]` on single-item arrays, so that they can round trip through a parse.
406
407When objects are stringified, by default they use bracket notation:
408
409```javascript
410qs.stringify({ a: { b: { c: 'd', e: 'f' } } });
411// 'a[b][c]=d&a[b][e]=f'
412```
413
414You may override this to use dot notation by setting the `allowDots` option to `true`:
415
416```javascript
417qs.stringify({ a: { b: { c: 'd', e: 'f' } } }, { allowDots: true });
418// 'a.b.c=d&a.b.e=f'
419```
420
421Empty strings and null values will omit the value, but the equals sign (=) remains in place:
422
423```javascript
424assert.equal(qs.stringify({ a: '' }), 'a=');
425```
426
427Key with no values (such as an empty object or array) will return nothing:
428
429```javascript
430assert.equal(qs.stringify({ a: [] }), '');
431assert.equal(qs.stringify({ a: {} }), '');
432assert.equal(qs.stringify({ a: [{}] }), '');
433assert.equal(qs.stringify({ a: { b: []} }), '');
434assert.equal(qs.stringify({ a: { b: {}} }), '');
435```
436
437Properties that are set to `undefined` will be omitted entirely:
438
439```javascript
440assert.equal(qs.stringify({ a: null, b: undefined }), 'a=');
441```
442
443The query string may optionally be prepended with a question mark:
444
445```javascript
446assert.equal(qs.stringify({ a: 'b', c: 'd' }, { addQueryPrefix: true }), '?a=b&c=d');
447```
448
449The delimiter may be overridden with stringify as well:
450
451```javascript
452assert.equal(qs.stringify({ a: 'b', c: 'd' }, { delimiter: ';' }), 'a=b;c=d');
453```
454
455If you only want to override the serialization of `Date` objects, you can provide a `serializeDate` option:
456
457```javascript
458var date = new Date(7);
459assert.equal(qs.stringify({ a: date }), 'a=1970-01-01T00:00:00.007Z'.replace(/:/g, '%3A'));
460assert.equal(
461 qs.stringify({ a: date }, { serializeDate: function (d) { return d.getTime(); } }),
462 'a=7'
463);
464```
465
466You may use the `sort` option to affect the order of parameter keys:
467
468```javascript
469function alphabeticalSort(a, b) {
470 return a.localeCompare(b);
471}
472assert.equal(qs.stringify({ a: 'c', z: 'y', b : 'f' }, { sort: alphabeticalSort }), 'a=c&b=f&z=y');
473```
474
475Finally, you can use the `filter` option to restrict which keys will be included in the stringified output.
476If you pass a function, it will be called for each key to obtain the replacement value. Otherwise, if you
477pass an array, it will be used to select properties and array indices for stringification:
478
479```javascript
480function filterFunc(prefix, value) {
481 if (prefix == 'b') {
482 // Return an `undefined` value to omit a property.
483 return;
484 }
485 if (prefix == 'e[f]') {
486 return value.getTime();
487 }
488 if (prefix == 'e[g][0]') {
489 return value * 2;
490 }
491 return value;
492}
493qs.stringify({ a: 'b', c: 'd', e: { f: new Date(123), g: [2] } }, { filter: filterFunc });
494// 'a=b&c=d&e[f]=123&e[g][0]=4'
495qs.stringify({ a: 'b', c: 'd', e: 'f' }, { filter: ['a', 'e'] });
496// 'a=b&e=f'
497qs.stringify({ a: ['b', 'c', 'd'], e: 'f' }, { filter: ['a', 0, 2] });
498// 'a[0]=b&a[2]=d'
499```
500
501### Handling of `null` values
502
503By default, `null` values are treated like empty strings:
504
505```javascript
506var withNull = qs.stringify({ a: null, b: '' });
507assert.equal(withNull, 'a=&b=');
508```
509
510Parsing does not distinguish between parameters with and without equal signs. Both are converted to empty strings.
511
512```javascript
513var equalsInsensitive = qs.parse('a&b=');
514assert.deepEqual(equalsInsensitive, { a: '', b: '' });
515```
516
517To distinguish between `null` values and empty strings use the `strictNullHandling` flag. In the result string the `null`
518values have no `=` sign:
519
520```javascript
521var strictNull = qs.stringify({ a: null, b: '' }, { strictNullHandling: true });
522assert.equal(strictNull, 'a&b=');
523```
524
525To parse values without `=` back to `null` use the `strictNullHandling` flag:
526
527```javascript
528var parsedStrictNull = qs.parse('a&b=', { strictNullHandling: true });
529assert.deepEqual(parsedStrictNull, { a: null, b: '' });
530```
531
532To completely skip rendering keys with `null` values, use the `skipNulls` flag:
533
534```javascript
535var nullsSkipped = qs.stringify({ a: 'b', c: null}, { skipNulls: true });
536assert.equal(nullsSkipped, 'a=b');
537```
538
539If you're communicating with legacy systems, you can switch to `iso-8859-1`
540using the `charset` option:
541
542```javascript
543var iso = qs.stringify({ æ: 'æ' }, { charset: 'iso-8859-1' });
544assert.equal(iso, '%E6=%E6');
545```
546
547Characters that don't exist in `iso-8859-1` will be converted to numeric
548entities, similar to what browsers do:
549
550```javascript
551var numeric = qs.stringify({ a: '☺' }, { charset: 'iso-8859-1' });
552assert.equal(numeric, 'a=%26%239786%3B');
553```
554
555You can use the `charsetSentinel` option to announce the character by
556including an `utf8=✓` parameter with the proper encoding if the checkmark,
557similar to what Ruby on Rails and others do when submitting forms.
558
559```javascript
560var sentinel = qs.stringify({ a: '☺' }, { charsetSentinel: true });
561assert.equal(sentinel, 'utf8=%E2%9C%93&a=%E2%98%BA');
562
563var isoSentinel = qs.stringify({ a: 'æ' }, { charsetSentinel: true, charset: 'iso-8859-1' });
564assert.equal(isoSentinel, 'utf8=%26%2310003%3B&a=%E6');
565```
566
567### Dealing with special character sets
568
569By default the encoding and decoding of characters is done in `utf-8`,
570and `iso-8859-1` support is also built in via the `charset` parameter.
571
572If you wish to encode querystrings to a different character set (i.e.
573[Shift JIS](https://en.wikipedia.org/wiki/Shift_JIS)) you can use the
574[`qs-iconv`](https://github.com/martinheidegger/qs-iconv) library:
575
576```javascript
577var encoder = require('qs-iconv/encoder')('shift_jis');
578var shiftJISEncoded = qs.stringify({ a: 'こんにちは!' }, { encoder: encoder });
579assert.equal(shiftJISEncoded, 'a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I');
580```
581
582This also works for decoding of query strings:
583
584```javascript
585var decoder = require('qs-iconv/decoder')('shift_jis');
586var obj = qs.parse('a=%82%B1%82%F1%82%C9%82%BF%82%CD%81I', { decoder: decoder });
587assert.deepEqual(obj, { a: 'こんにちは!' });
588```
589
590### RFC 3986 and RFC 1738 space encoding
591
592RFC3986 used as default option and encodes ' ' to *%20* which is backward compatible.
593In the same time, output can be stringified as per RFC1738 with ' ' equal to '+'.
594
595```
596assert.equal(qs.stringify({ a: 'b c' }), 'a=b%20c');
597assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC3986' }), 'a=b%20c');
598assert.equal(qs.stringify({ a: 'b c' }, { format : 'RFC1738' }), 'a=b+c');
599```
600
601## Security
602
603Please email [@ljharb](https://github.com/ljharb) or see https://tidelift.com/security if you have a potential security vulnerability to report.
604
605## qs for enterprise
606
607Available as part of the Tidelift Subscription
608
609The maintainers of qs and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-qs?utm_source=npm-qs&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
610
611[package-url]: https://npmjs.org/package/qs
612[npm-version-svg]: https://versionbadg.es/ljharb/qs.svg
613[deps-svg]: https://david-dm.org/ljharb/qs.svg
614[deps-url]: https://david-dm.org/ljharb/qs
615[dev-deps-svg]: https://david-dm.org/ljharb/qs/dev-status.svg
616[dev-deps-url]: https://david-dm.org/ljharb/qs#info=devDependencies
617[npm-badge-png]: https://nodei.co/npm/qs.png?downloads=true&stars=true
618[license-image]: https://img.shields.io/npm/l/qs.svg
619[license-url]: LICENSE
620[downloads-image]: https://img.shields.io/npm/dm/qs.svg
621[downloads-url]: https://npm-stat.com/charts.html?package=qs
622[codecov-image]: https://codecov.io/gh/ljharb/qs/branch/main/graphs/badge.svg
623[codecov-url]: https://app.codecov.io/gh/ljharb/qs/
624[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/ljharb/qs
625[actions-url]: https://github.com/ljharb/qs/actions