2

A typical search query looks something like

GET example.com/entity/search?q=John

If we want to add some filtering to this endpoint, we could for example add ...&filter= followed by a URL encoding of something like

{
  "and": [
    {
      "equals": {
        "field": "lastName",
        "value": "Smith"
      }
    },
    {
      "greaterThan": {
        "field": "age",
        "value": 20
      }
    }
  ]
}

In this case however, the query string may become arbitrarily long, and for technical reasons you shouldn't really exceed ~2000 characters.

For this reason, some people suggest using POST even though it's arguably not an ideal choice from a semantical point of view.

So, I'm considering allowing both GET and POST:

  • For queries that fit in a URL: Use GET for clarity, ease of debugging, and semantically accurate requests.
  • For queries that exceed maximum length of a URL: Use POST and pass query / filtering as body.

Apart from requiring slightly more code and documentation, I can't really see any drawbacks. OTOH I don't think I've stumbled across this approach when reading API documentation.

My questions:

  • Is there any prior art of such solution in a public "widely available" API?
  • Are there any drawbacks I'm missing?
aioobe
  • 957

3 Answers3

3

To handle this case, one can potentially use a POST to send all the information (the parameters in the body) for the query. Then the return from the POST is a query "search token". Then call the GET with the search token.

GET /widgets?search={token_goes_here}

The GET will retrieve all the search parameters for that token from the original POST and then do the search. With this method one is following the semantics of POST and GET when the amount of search parameters exceeds the URL length.

Do not send a body with a GET, even if it works because it may not be universally supported.

Jon Raynor
  • 11,773
1

The http method is just another header. You can ignore it and have your API endpoint accept, json body, query parameters and multipart form data etc depending on what is sent, regardless of whether its considered semantically correct.

With the query string limitations and the fact that they and the path are often logged by default. The body is the first choice for sending data.

There are no drawbacks to using POST /ReturnTheThing style endpoints other than a human can't browse to them as easily.

The question of whether to have a fall back method is more complex. If i have a parameter which is a list, I allways send it in the body because its a bug if it longer than the 2k characters.

Now if I have two supported methods of doing the same thing, and one has a bug. Why would I implement them both in a client library?

Ewan
  • 83,178
0

From a design standpoint, I'd avoid having two ways of doing the same thing. If GET isn't a good fit to cover all use cases, don't use GET.

When designing an API, your goal is to make it as simple as possible for the client to use. For example, if when editing a resource a specific change to a property would cause the request to not be idempotent, the request should either be marked as always POST or not allow that property in the body of a PUT request. The client should not have to check if that specific property matches some specific state to decide weather to send a POST or PUT request, it should just have to decide weather a specific request should include that property or not.

There are various technical reasons why APIs sometimes may use POST instead of GET for retrieving resources. It can be because the parameters are sensitive, in which case it's better put them in the body of the HTTPS request so it gets encrypted and not shown in any logs. Or it can be because the API is built with another thing on top of HTTP, like GraphQL, where POST is used as the most generic HTTP verb. In fact GraphQL uses POST for querying probably in part because of the same scenario you described.

The main drawback for using POST instead of GET are:

  • lack of HTTP caching (I'm not 100% sure if something may still be possibly used here, but don't count on it to be supported everywhere)
  • links are not sharable (i.e. you can't just copy paste the URL to run the same query)

If you need any of these features, you will need to build it in the app. Caching will obviously depend on what technologies you are using, and for shareable links you will need to built some proxy link feature.

5ar
  • 156