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 or null).
  • The parse result of an empty string is ambiguous since it may stem from ?key= or ?key.

References

  1. URI.js
  2. Node.js
  3. Purl