{"id":15772,"library":"qs","title":"qs: Querystring Parser and Stringifier","description":"qs is a robust JavaScript library for parsing and stringifying URL query strings, with comprehensive support for nesting objects and arrays. It is currently on version 6.15.1 and maintains a steady release cadence with a focus on stability and security patches. Key differentiators include its configurable depth limits for parsing, the ability to handle URI-encoded strings, and built-in protections against prototype pollution through options like `plainObjects` and `allowPrototypes` (which is dangerous if enabled). Unlike the native `querystring` module in Node.js, `qs` offers more advanced features like array indexing and custom parsing/stringifying logic, making it suitable for complex data structures often found in web applications.","status":"active","version":"6.15.1","language":"javascript","source_language":"en","source_url":"https://github.com/ljharb/qs","tags":["javascript","querystring","qs","query","url","parse","stringify"],"install":[{"cmd":"npm install qs","lang":"bash","label":"npm"},{"cmd":"yarn add qs","lang":"bash","label":"yarn"},{"cmd":"pnpm add qs","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"CommonJS is the primary documented import style. While `import qs from 'qs'` generally works in modern environments due to bundler/Node.js interop, `require()` is explicitly shown in documentation examples and offers more direct compatibility.","wrong":"import qs from 'qs';","symbol":"qs","correct":"const qs = require('qs');"},{"note":"The `qs` module exports a single default object. Named imports like `import { parse } from 'qs'` will not work directly as `parse` is a method of the default exported object, not a top-level named export. Destructuring from the `require`'d object is the correct way.","wrong":"import { parse } from 'qs';","symbol":"qs.parse","correct":"const { parse } = require('qs');"},{"note":"`stringify` is a method on the default `qs` object. Attempting to directly import it as a named export will result in an error.","wrong":"import { stringify } from 'qs';","symbol":"qs.stringify","correct":"const qs = require('qs');\nconst stringify = qs.stringify;"}],"quickstart":{"code":"const qs = require('qs');\nconst assert = require('assert');\n\n// Parsing a simple querystring\nlet obj = qs.parse('a=c&b=d');\nassert.deepEqual(obj, { a: 'c', b: 'd' });\n\n// Stringifying an object\nlet str = qs.stringify({ a: 'c', b: 'd' });\nassert.equal(str, 'a=c&b=d');\n\n// Parsing a nested object\nobj = qs.parse('foo[bar]=baz');\nassert.deepEqual(obj, { foo: { bar: 'baz' } });\n\n// Parsing an array\nobj = qs.parse('a=b&a=c');\nassert.deepEqual(obj, { a: ['b', 'c'] });\n\n// Demonstrating depth limit (default 5)\nconst deepString = 'a[b][c][d][e][f][g]=h';\nobj = qs.parse(deepString);\nassert.deepEqual(obj, {\n    a: {\n        b: { c: { d: { e: { f: { '[g]': 'h' } } } } }\n    }\n});\n\nconsole.log('All assertions passed!');","lang":"javascript","description":"Demonstrates basic `qs.parse` and `qs.stringify` functionality, including nested objects, arrays, and the default depth limit behavior."},"warnings":[{"fix":"If nested object parsing via dots is desired, set `qs.parse(str, { allowDots: false })`. If you were relying on `{ 'a.b': 'c' }` behavior and upgraded from v5, ensure your code handles the new default.","message":"The `allowDots` option's default value changed from `false` to `true` in `qs` v6.0.0. This means by default, query strings like `a.b=c` will now parse into `{ 'a.b': 'c' }` instead of `{ a: { b: 'c' } }` in previous versions. If you relied on dot notation for nested objects, you might need to explicitly set `allowDots: false` or update your parsing logic.","severity":"breaking","affected_versions":">=6.0.0"},{"fix":"Always explicitly set `arrayLimit` in `qs.parse(str, { arrayLimit: <your_desired_limit> })` if you expect arrays with a variable or large number of elements to prevent unexpected truncation.","message":"The default `arrayLimit` for parsing arrays changed multiple times across major versions (e.g., to 20 in v5, and later). If your application parses arrays with more than the default limit of items (e.g., `a=1&a=2&...&a=21`), elements beyond the limit will be truncated or ignored, potentially leading to data loss if not handled.","severity":"breaking","affected_versions":">=5.0.0"},{"fix":"For deeply nested structures, provide a higher `depth` option to `qs.parse(string, { depth: <max_depth> })`. Consider also using `strictDepth: true` to throw an error instead of truncating, making unexpected depth explicit.","message":"By default, `qs.parse` limits object nesting depth to 5 to prevent potential Denial of Service (DoS) attacks from excessively deep query strings. If a query string exceeds this depth, subsequent nested keys are concatenated into a single key, leading to unexpected parsed object structures.","severity":"gotcha","affected_versions":">=0.6"},{"fix":"NEVER set `allowPrototypes: true` with untrusted user input. By default, `qs` prevents this. If you need to handle keys like `__proto__`, `constructor`, or `prototype` as actual data keys, use `plainObjects: true` to return a null-prototype object (`Object.create(null)`), which isolates the parsed data from the global `Object.prototype`.","message":"Setting `allowPrototypes: true` in `qs.parse` or `qs.stringify` can introduce severe prototype pollution vulnerabilities. This option allows user-controlled input to modify properties on `Object.prototype`, which can impact all objects in the application and lead to remote code execution or other critical security flaws.","severity":"breaking","affected_versions":">=0.6"}],"env_vars":null,"last_verified":"2026-04-21T00:00:00.000Z","next_check":"2026-07-20T00:00:00.000Z","problems":[{"fix":"For CommonJS, use `const qs = require('qs'); const obj = qs.parse('...');`. For ESM, use `import qs from 'qs'; const obj = qs.parse('...');`.","cause":"Attempting to use `qs.parse` or `qs.stringify` as a named import (e.g., `import { parse } from 'qs';`) instead of accessing it as a property of the default exported `qs` object.","error":"TypeError: qs.parse is not a function"},{"fix":"Either increase the `depth` option to accommodate the expected nesting level (`qs.parse(str, { depth: <new_depth> })`) or adjust the input to reduce nesting. If truncation is acceptable, remove `strictDepth: true`.","cause":"The parsed querystring exceeded the maximum allowed nesting depth (`depth` option), and the `strictDepth` option was set to `true`, causing an error to be thrown instead of silently truncating the nested keys.","error":"Input depth exceeded depth option of X and strictDepth is true"},{"fix":"To parse `a.b=c` into `{ a: { b: 'c' } }`, use `qs.parse(str, { allowDots: false })`. To handle deep nesting, adjust the `depth` option (e.g., `qs.parse(str, { depth: 10 })`).","cause":"This often occurs due to misunderstanding the `allowDots` or `depth` options. For instance, `allowDots` defaults to `true` in v6, preventing dot notation from creating nested objects by default. The `depth` limit can also cause key concatenation.","error":"Unexpected parsed object structure, e.g., `{ 'a[b]': 'c' }` instead of `{ a: { b: 'c' } }` or `{ 'a.b': 'c' }` instead of `{ a: { b: 'c' } }`"}],"ecosystem":"npm"}