Encoding of null and undefined in a type-safe URI query
TL;DR
When transferring variables in the query part of an URI, encode the JavaScript value null
in key=null
as ?key
. Distinguish between undefined
, null
and an empty string by omitting the key altogether for undefined
and appending an equal sign for the latter. I.e. ?key=
stands for key=""
. Use the URI.js as helper library for URI query extraction and composition.
The Problem
The standard way of encoding of data in the query part of an URI in the format
"?key1=value1&key2=value2..."
is created for the purpose of submitting a HTML form via a HTTP GET request (with content type application/x-www-form-urlencoded
).
In general the types of the variables encoded in this way are not preserved.
Imagine for instance that booleans could get encoded as true
and false
making then indistinguishable from strings with those values.
Type-safe URI query for null
, undefined
and ""
Outside the use for HTML forms the syntactic format of the query part of a URI is largely by convention.
From the URI rfc:
The query component is indicated by the first question mark („?“) character and terminated by a number sign („#“) character or by the end of the URI.
http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ] "#" fragment ]
Thus the query part can contain anything except the number sign („#“).
URIs containing query components like ?key
and ?key=
for a variable key
are valid URIs. This can be leveraged to fix type safety for null
values.
The problem however is how these formats are interpreted by different query component parsers.
Note: This discussion just applies to cases where you are forced to resort to the HTTP GET query component to transmit variables which can be null
or undefined
. In general it is advised to use a more suitable format like JSON for more type-safe en/decoding of your data.
Choosing the right query component builder/parser library
URI.js
Values of null
or undefined
are invariant with respect to URI query component encoding-decoding.
// composing
URI.buildQuery({keyN: null, keyU: undefined, keyS: ""}); // <-- original
"keyN&keyS="
// extracting
URI.parseQuery("keyN&keyS=");
{keyN: null, keyS: ""} // <-- parsed
Node.js, jQuery-Purl
Node.js' url module as well as the combination of jQuery and Purl coalesce null
, undefined
and ""
into ?key=
. which in turn always get decoded as empty string.
///// Node.js /////
url = require('url');
// composing
url.format({query: {keyN: null, keyU: undefined, keyS: ""}}); // <-- original
'?keyN=&keyU=&keyS='
// extracting
url.parse("//s/?keyN=&keyU=&keyS=", true).query
{ keyN: '', keyU: '', keyS: '' } // <-- parsed
// composing with jQuery
$.param({keyN: null, keyU: undefined, keyS: ""}); // <-- original
"keyN=&keyU=&keyS="
// extracting with purl.js
purl("//s/?keyN=&keyU=&keyS=").param();
{keyN: "", keyU: "", keyS: ""} // <-- parsed
- The query part
?key=
is ambiguous (can result from an empty string,undefined
ornull
). - The parse result of an empty string is ambiguous since it may stem from
?key=
or?key
.