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