I recently had a good discussion with my colleagues on a REST (piggybacking on HTTP) service API design. Quickly a very simple question piqued the interest of us all – “what information should be requested in the (custom) headers and what information should be requested in the query string”.
Taking a step back, it is worth asking why this question would even arise? It is because both, http headers and query string parameters, are valid input vehicles for a REST resource that influence the response it returns.
It is helpful to take one more step back to bring in two REST concepts to the fore to set up the context for our answer:
- A single resource could be a collection of entities. Fielding defines “a resource R is a temporally varying membership function MR(t), which for time t maps to a set of entities, or values, which are equivalent.”
- A REST ecosystem is a collection of resources.
We should examine a few characteristics (below) of the parameter under consideration to find a home for it:
To Restrict or To Offer Filters
As mentioned earlier, a resource is a collection of entities. Both headers and query string parameters play a role in selecting a subset of entities from the collection. If the need is to restrict or preselect a subset unbeknownst to the client, then headers should be used. On the other hand, if the need is to allow the client to choose a subset, then query string parameters must be used.
For example: The /orders is a resource of order entity collection in a store.
- When a customer accesses /orders, he/she is only presented with the orders he/she created.
- However, when the store manager accesses /orders, he/she is presented with all the orders.
In the above case, the resource pre-filtered the order entity collection by using the contextual information (store manager or customer).
Query strings can be used by both of them further narrow the order entities in this fashion /orders?date=today
- The customer is presented with the orders he/she placed today.
- The store manaager is presented with all the orders by all customers placed today.
In the above case, the user (store manager or customer) is specifically request a subset of the order entities.
More than filtering
The sweet spot for query string parameters is resource filtering (like the cases we talked about earlier). For other requirements we need to start from headers and work towards query string parameters (i.e if we have to).
HTTP offers a wide variety of standard headers addressing a wide variety of needs:
- Some, like Content-Type, describe the representation used.
- Some others, like If-Modified-Since, influence resource caching.
- And only a few, like Authorization and Range, could have an “effect” similar to filtering of collections of entities.
HTTP headers should be used to send contextual information about the request to the resource.
There is nothing in REST specification that limits us to the standard headers. It should be fine to roll our own custom header in case of a specific justifiable need – HTTP itself is designed to be extensible.
Specificity to a resource
Query string parameters have a strong correlation to the properties of the entity behind a resource. For example: “order_date” property belongs to a the order entity and it is a natural fit for a query string. This fits in nicely the filtering view of resources – obviously it logical to filter entities with the properties on it.
On the other hand, headers seldom have a direct correlation to specific properties of the entity behind a resource.
If the parameter under consideration has a strong affinity to a resource, then placing it in the header is not the right choice.
Sphere of influence
Query string’s specificity to a resource also limits it’s influence (so simply reusability) to a single source (very few times more than one).
In the cases where the parameter under consideration is expected to be applicable to all resources in the ecosystem, then locating this parameter in the header makes most sense. It is worth emphasizing that “being applicable” is not the same as mandatory.
Another hint one could use to determine if a parameter belongs to in the query string or the header is by asking who/when/where provides the value for the parameter.
Header parameters are typically added out-of-band by applications like user agents and/or proxies. If you need to allow message intermediaries to add the parameter to the message, then it should be located in the header.
In case of query string, parameters can usually be traced back to some kind of direct input accepted by the system to satisfy a request. Query string remains untouched by intermediate proxy layers.
In other words, query string is reserved for end users/applications that are invoking resource. On the other hand headers are reserved for applications and intermediaries that are facilitating this invocation.
At the core of it, to locate parameter in a header or a query is not an exact science. However, the above guidelines could help lending some objectivity to most decisions.