Query-string parser lab · Updated November 9, 2026
Query Parameters Explained: Repeated Empty and Array Values
A query string is the key-value sequence after the ? in a URL, with parameters separated by &. Repeated keys, empty values, valueless keys, and array conventions can make the same data parse into different results, so inspect the raw sequence before choosing a parser.
Query string URL anatomy
In /search?tag=red&tag=blue#results, the query string is tag=red&tag=blue. The ? starts it, & separates parameters, and #results is a fragment for the client; the fragment is not sent in the HTTP request.
Repeated, empty, and valueless parameters
| Raw input | State | JavaScript | Python |
|---|---|---|---|
tag=red&tag=blue | Repeated key | get() → first; getAll() → both | parse_qs → list |
empty= | Empty value | key exists, value "" | parse_qs(...,keep_blank_values=True) |
flag | Valueless key | key exists with empty value | Node returns an empty string; Python default drops it, and keep_blank_values=True retains [""]; see the measured table below |
Do not convert repeated entries to a plain object before deciding what they mean: a single object property cannot retain every value.
Arrays and objects: three serialization conventions
The same logical list can be written three common ways. They are not interchangeable on the wire.
| Convention | Raw query | Typical parser result |
|---|---|---|
| Bracket | a[]=1&a[]=2 | Array under the key a in bracket-aware frameworks |
| Repeated key | a=1&a=2 | List when the parser preserves duplicates; first value in single-value APIs |
| Comma value | a=1,2 | One string unless the application explicitly splits commas |
Language-level measurements
Run date: November 9, 2026. Node.js v24.19.0, Python 3.13.15. PHP 8.5.12 CLI was used for the PHP row.
| Runtime | a[]=1&a[]=2&flag | flag |
|---|---|---|
| Node URLSearchParams | keys remain literal a[]; entries: [["a[]","1"],["a[]","2"]] | get("flag") → "" |
Python 3.13 parse_qs default | {"a[]":["1","2"]}; bracket is not special | omitted by default |
Python 3.13 parse_qs(..., keep_blank_values=True) | {"a[]":["1","2"]} | {"flag":[""]} |
Node qs 6.13.1 | {a:["1","2"]} | flag:"" |
PHP parse_str | a array ["1","2"]; flag empty string | flag → "" |
The important mismatch is concrete: Python keeps the literal key a[], while bracket-aware conventions turn it into a. A parser choice is therefore part of the API contract.
Measured raw request and parsed output
GET /items?a[]=1&a[]=2 HTTP/1.1
raw query: a[]=1&a[]=2
bracket-aware output: {"a":["1","2"]}
GET /items?a=1&a=2 HTTP/1.1
raw query: a=1&a=2
get(): "1" getAll(): ["1","2"]
GET /items?a=1,2 HTTP/1.1
raw query: a=1,2
plain output: {"a":"1,2"}These outputs describe parser behavior, not a universal server requirement. The server or framework decides which convention it accepts.
Decode URL parameters
Percent-encoded parameter names and values must be decoded with the correct plus policy. Use URL Decode Online for one-layer decoding and to compare + with %20.
Encoding-side pitfalls
For form bodies, see application/x-www-form-urlencoded. For repeated-key serialization and mutation behavior, see repeated query parameters.
Run the parser
FAQ
Are repeated keys valid?
They are syntactically possible, but the application must define whether to keep all values or only one.
Is an empty value the same as a missing value?
No. empty= carries a key with an empty value; a missing key is absent from the query.
Does the fragment reach the server?
No. The browser keeps the fragment client-side and excludes it from the HTTP request target.
Which array convention should I choose?
Choose the convention your server parser documents, then test its raw request and parsed output.