Don't use GET for APIs

2 points by themihai ↗ HN
I have an Web API developed using the http methods as they were intended[0] by the authors. Now I need to enhance the GET based API calls with support for batch requests. It turns out that there is no standard method to encode more than name -> value pairs in the query string. Everything is framework, language specific. So if you need more(e.g. an array or a nested data structure) than name value pairs you better avoid GET. I hope this helps anyone who starts a new Web API project.

[0] https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

12 comments

[ 3.0 ms ] story [ 30.3 ms ] thread
This would honestly work better as an 'ask HN', rather than a 'tell HN'.

Namely, if this were an Ask HN, I'd tell you to use the new HTTP RFCs [1] instead of the old ones.

I'm assuming that 'batch requests' means that your HTTP API is a thin proxy that surfaces data from some underlying database, and you want to enhance to be able to operate on more than one underlying resource. In the case of GET, you're retrieving data, so I'm guessing you want to surface more than one resource. I would call that a 'search', while modifying more than one resource with the same request I'd call a 'batch'; please correct me if you feel I'm wrong.

With that in mind, I suggest two approaches to have 'search requests':

[A.] try to express your search filters with key-value pairs, as fundamentally that's what should be going on behind the scenes.

[B.] design a 'Search Manager' endpoint where you can POST a search-request in a complex datatype of your design. This endpoint returns a 201 Created with a Location URI that allows you to retrieve the results.

The suggestion in this programmers exchange post is essentially the same, stated slightly differently [2].

[1] https://tools.ietf.org/html/rfc7231#section-4.1

[2] http://programmers.stackexchange.com/questions/233164/how-do...

I don't think it's search because the server must provide a response for each resource or an error. If one resource is missing the server should return an error. --- Concerning the post I would move it to `ask HN` but I don't know how to do that. Perhaps I need more karma?
If you need separate responses for each resource, why not make separate requests in the first place?

- OR -

You can design a structured format that communicates the information you want. For example, a format based on JSON with an array of objects, or a format based on JSON with an key-value map of objects (ie. object of objects).

Well that's exactly what I need: A format that communicates structured data, friendly with GET requests and backward compatible with query string encoding. Why there is no such format/standard yet? Is it that unusual?
There are ready-made mediatypes to communicate collections; some have even been registered as mediatypes with IANA. A simple one that would meet your needs is Collection+JSON [1].

There are others. This blog post [2] from 2014 lists some from a hypermedia perspective, although you may find some of these formats excessive for what you're trying to accomplish.

Before JSON became a favorite for HTTP APIs, there used to be XML-based ones as well. AtomPub is a very good protocol, which defines an 'application/atomcoll+xml' mediatype for collections [3]

[1] http://amundsen.com/media-types/collection/

[2] http://sookocheff.com/post/api/on-choosing-a-hypermedia-form...

[3] http://bitworking.org/projects/atom/draft-ietf-atompub-proto...

That's cutting off your nose to spite your face. It's also cutting off your cache! Most caching semantics are based on GET requests.

Why can't use just encode a complex type and parse it at the receiving end? The url may look like garbage, but you could theoretically pass anything you want (within length bounds) as a parameter.

I've started doing just that but I have a micro-services architecture so I need to implement the custom encoding schema on multiple stacks (i.e. java, python, go) which takes time. Being non-standard the external consumers will have to use or reimplement our encoding schema. Feels like reinventing the wheel. I'm still can't get why nobody published a RFC for this issue. PayPal[0] uses query string extensively and still the format is driven by example and implementations(i.e. their SDKs)

[0] https://developer.paypal.com/docs/classic/api/NVPAPIOverview...

Are you using the same data format for all platforms? You could try writing a test framework in a BDD-language like Cucumber that can be ported to multiple languages, and code a language-specific parsing library against that. It's still work, but not that bad, and it would keep things consistent.

Even without that, passing a parameter as url-encoded json isn't that awful.

> Feels like reinventing the wheel. I'm still can't get why nobody published a RFC for this issue.

They have, back in the "XML is the king of data formats, and WebDAV is the way to build web applications" days. (It sounds something like either the SEARCH or REPORT HTTP method defined for WebDAV.)

Someone proposed a generified version of SEARCH but it didn't go anywhere, that and a few other bits of WebDAV really could use being detached and made useful separately for generic web content.

It's ugly but i've seen it done - you could pass JSON objects as parameter values.
ElasticSearch [1] is one of the few APIs that does this. This is not a good idea [2], because you're sure to break caching and proxying, if it works at all.

Even ElasticSearch acknowledges this and allows you to POST the body instead. That's not very 'RESTful' either, but at least it's to spec. But since you haven't mentioned REST at all, POSTing to a controller resource to perform what would amount to be a retrieval is certainly a common practice in other HTTP APIs.

[1] https://www.elastic.co/guide/en/elasticsearch/reference/curr...

[2] http://stackoverflow.com/questions/978061/http-get-with-requ...

Oh boy. You have just found that you could be better off with a proper RPC protocol.