JSON in URLs

HTTP-based APIs often encode arguments as URL path and query parameters. For example, a call to the Dropbox API’s filename search endpoint might look like:

/1/search/auto/My+Documents?query=draft+2013

While URL encoding seems fine for simple examples, using JSON might have some advantages.

URL paths are complicated

In the example above, the first “+” is a literal plus sign because it’s in the URL. The second “+” represents a space because it’s in the URL query component. It’s easy to confuse the two since the encoding rules are mostly the same and sometimes the library functions are name something ambiguous like “urlencode”. An early version of one of our SDKs had a bug because of this.

Another common mistake is assuming that a “/” in the path component is just like any other character.

/hello-world” is equivalent to “/hello%2Dworld
/hello/world” is not equivalent to “/hello%2Fworld

The “/” character is a reserved delimiter. Changing it to its percent-encoded form can change the meaning of the URL. Most URL encoding libraries don’t make it clear how important the distinction can be. They get away with this because a lot of the time it doesn’t matter. However, sometimes it matters a lot.

URL query parameters are not as expressive

What if one of your API call arguments is a list of values? Some APIs handle this by using commas or repeated names.

/docs/salary.csv?columns=1,2
/docs/salary.csv?column=1&column=2

And for nested object fields, some APIs do things like:

/emails?from[name]=Don&from[date]=1998-03-24&to[name]=Norm

These are all reasonable workarounds, but they’re still workarounds. There’s no widespread standard. JSON handles nesting in a consistent and straightforward manner.

Is URL encoding just bad?

Nope. It’s just designed for a different situation: human interactive use.

JSON makes you quote every string. That makes things simpler and more robust, but also makes things a more tedious for a human to read and write.

URL parameters are much quicker to whip up, and for the common case, that’s fine. The downside is that there’s a higher risk of messing something up. And any code that deals with URL parameters ends up more complicated because of it.

These are reasonable tradeoffs. I wouldn’t want to use JSON in my browser address bar. But an HTTP-based API might be better served by something closer to JSON’s spot on the spectrum.

So what’s the problem?

When you think about it, we’ve already all sort of decided that JSON is better than URL encoding for some things. HTTP API responses for structured data are almost always JSON. The last time I dealt with a URL encoded response body was for OAuth 1, which was finalized in 2007. OAuth 2 switched to JSON.

API request bodies seem split between JSON and URL encoding. One nice thing about URL encoding is that you can have nice “curl” command-line examples. But many APIs, including Dropbox’s newer APIs, have been moving toward using JSON in the request body.

So why not use JSON in the URL as well? Well, there’s this:

URL encoded: /log?a=b&c=4
JSON in URL: /log?%7B%22a%22:%22b%22,%22c%22:4%7D

For one, it’s much longer. This can become a problem if it starts pushing you past the practical URL length limit.

It also looks ugly, but that can be solved by taking abstraction seriously. For example, you don’t ever deal with raw network packets unless something goes wrong at the packet level. Similarly, you shouldn’t have to deal with the ugly version of the URL unless you have an invalid URL. Once it’s past that check, you should only see the decoded string in any error messages or logging output.

Creating a clean abstraction takes extra work, especially if you’ve been used to getting away without it. Using JSON in your URLs will have a bunch of up-front annoyances to resolve, but the overall benefits might make it worth trying.