JustCRecently came across a post on LinkedIn related to query params in URI. In very simple words, the...
Recently came across a post on LinkedIn related to query params in URI. In very simple words, the problem was related to GETting alternate representation of a resource.
Let's take an example of simple api call
Request:
GET /users/1
Response:
HTTP/1.1 200 Requested user details
Content-Type: application/json
{"username": "Name", "displayName": "Display Name"}
If requester is only interested in displayName, the request can be made as.
Request:
GET /user/1?fields=displayName
Response:
HTTP/1.1 200 Requested user details
Content-Type: application/json
{"displayName": "Display Name"}
It looks reasonable. It works.
But what exactly are we asking the URI to represent?
The request simply means, we are looking for a way to apply projection to retrieve only the displayName and it seems legit. Is it though?
Let's look at RFC 3986, especially the part where it talks about syntax of URI or Uniform Resource Identifier.
A URI is composed of five main components, following this hierarchical pattern:
scheme ":" hier-part [ "?" query ] [ "#" fragment ]
Still seems legit until you start see more such patterns
GET /users/1?fields=displayName
How about
GET /users/?sort=
or
GET /users/?filter=
Let's rewrite the the first example little differently
GET /users?id=1&fields=displayName
Now the oddity starts to become visible. id is, obviously, the search param, but fields is a projection. Query params sort, fields, filter, etc. are used as mini-instructions on top of limited set of HTTP verbs. People start using it, and it literally starts shaping up as a standard.
Not really. RFC 3986 is clear that stuff following "?" is just bunch of key-value pairs. In fact the entire URI is opaque, if you decipher the RFC. So much so, you would hear
a good REST API treats the query string as a structured DSL (Domain Specific Language) for the resource.
That's the beauty of ReST. Being just a guideline, you see such pragmatic shortcuts that work in real life and often more useful.
RFC allows it.
The concern is not correctness, it's architectural drift.
I must present a different take on the topic, otherwise this post is meaningless. The DSL keywords sort, fields, filter, etc. start crowding the limited set of verbs, and I, as a developer of API, am now forced to maintain documentation around them. Also is a fact that these mini-instructions become part of set of reserved words.
If projection is a representation concern rather than resource selection, then HTTP already provides a mechanism: content negotiation.
GET /users/1
Accept: application/json; fields=displayName
You might have seen this in a different form as
GET /users/1
Accept: application/json; q=0.9;charset=UTF-8
The string following the media type can be used to convey additional hints about the media-type interpretation. The key-value pairs that trail the media-type are conveniently called as media-type parameters. These are open-ended too, you can have as many as you prefer, just have server implementation to process them.
You want more standard driven approach, follow RFC 6906. It adds more structure to asking for representation variations
GET /users/1
Accept: application/json; profile="http://www.example.com/profiles/user-summary"
Or you can come up with your own custom media-type
GET /users/1
Accept: application/vnd.user.displayname+json
There are few very practical reasons rooted in the way web works.
Links are essential part of creations responses. It is much easier to convey the newly created url as
Location: /user/1?fields=displayName
than bunch of additional instructions to the client on top of regular GET. You would need to enrich the response with more headers.
Caches are not smart enough to understand these nuances. One has to sprinkle "Vary:" headers to make caches understand what to cache. It is more of hit and miss with different cache implementations.
Purpose of this post is not advocacy of purist approach. I just want to make a point that "Common practice does not automatically make it architectural practice." The roots should not be forgotten and pragmatic shortcuts must be explicitly registered as shortcuts than standard. Once the real reason for the shortcuts is forgotten, soon a slippery slope makes it faster to lose the grip of standards.
“Architecture erodes not through wrong decisions, but through forgotten reasons.”