0

I'm trying to create an API that follows REST best-practices, is intuitive, and matches what patterns developers see from other (well-designed) APIs. I believe I understand the various mapping of the HTTP verbs to the typical resources, which is in line with the answers in these two questions:

Which HTTP methods match up to which CRUD methods?

Understanding REST: Verbs, error codes, and authentication

Where I'm having trouble is that, due to the nature of our business and the data required to retrieve data, which would normally be a GET, we need to use a POST. This is due to both the size of the request, as well as what we're passing in order to do the search.

When do you use POST and when do you use GET?

So, there are a couple approaches I can think of on how to best handle CRUD, and I've love some suggestions:

  1. Use query parameters in the POST for differentiating CREATE vs READ, other verbs as normal. Don't love this idea.
  2. Have a separate endpoint for each action, e.g. /baseUrl/lookup, /baseUrl/create. Doesn't follow the proper pattern of using nouns instead of verbs.

EDIT: In the interest of clarity, the contrived example in my comments of an image lookup service, where a caller can search if an image is already in the database, add a new image, update the image (e.g. add metadata), or delete the image.

Create: What should we do here? POST /image/create and update the Read endpoint to /image/search?

Read: POST /image { imageData = someBase64EncodedImage }

Update: PUT /image/{imageId}

Delete: DELETE /image/{imageId}

Community
  • 1
  • 1
Matt
  • 2,682
  • 1
  • 17
  • 24
  • We need a more down to earth example. That said, the issue of having to use POST when GET is obvious sounds like you are retrieving a list of a resource with explicit information in the query string (stuff like IDs) which is solved using pagination – JSelser Feb 28 '17 at 13:36
  • As an example of something similar, let's say the Google Reverse Image search, though in our case it's really just returning the best match. So you need to POST an image to see if there is an image that is a very close match (within some threshold), otherwise nothing is returned. You may also want to add an image to the database, to be retrieved at a later time. IT's not really an issue of pagination, or too much data being returned. It's that it's messy to use POST for both creation and retrieval. – Matt Feb 28 '17 at 13:44
  • You may want to update your question with an example that most closely resemble your issue as to get better answers. In particular I can't infer which is your resource by context, is it images then? – JSelser Feb 28 '17 at 14:01
  • It is not images, but it's close enough to really not be relevant. Regardless I don't think that a concrete example is necessarily the problem here. The question is, broadly, how does one handle creating a resource when POST is already used for something else, and I need to reserve PUT for updates. The best practices and guidelines I've come across in my research haven't had a uniform answer to this question, so I want to see how people have solved this problem across a variety of different APIs – Matt Feb 28 '17 at 14:06

1 Answers1

1

I think that there's a misunderstanding about using POST for retrieving resources. From a high point of view, you state a problem as follows:

I need to retrieve a list of entities from the server that match a lot of parameters. How can I achieve this?

Thus, the tempation of seeing a POST as a mean for requesting a READ is strong. And, at the same time, is misleading. Everywhere you hear REST evangelists swearing that a READ should be implemented via a GET and tunneling operations using POST is a REST antipattern.

The solution to the problem is very simple, and it's about changing our approach. It's not POSTing for READing. It's about submitting a request ticket to the server, waiting for a response back. And, in fact, it is definitely what we are doing using a POST. A POST, creates a subresource, period (yep, it can also be used for updating, but I don't want to get lost in details).

So, if you want to query a server for a complex resource, just place a ticket and wait for a response. Let's examine a use case:

you have a car resource identified by the following URI

http://authority/api/cars/{id}

and, say, you want to query the server in a very complex way by issuing a POST.

What endpoint will you use? Definitely you can't go with

POST http://authority/api/cars

because if you do so, what you expect to obtain is nothing but a car creation, isn't it? The solution is, again, very simple. You are trying to submit a query ticket to the sever so you should deign this resource. You could be more creative, but maybe this could work

POST http://authority/api/tickets/cars

POSTing a query ticket to this endpoint you can expect to have a response back with a location header referring to a list of cars (a cars resource) that match your request (status code 201 = creted or 202 = accepted, the result will be ready asap). If the computation is fast enough it's not a deadly sin to include the result in the HTTP response (personal opinion).

For a more comprehensive dissertation, please refer to this brilliant article: How to GET a Cup of Coffee.

MaVVamaldo
  • 2,505
  • 7
  • 28
  • 50
  • Added my example explicitly in the original question. The article you linked makes sense to me, and I think I came across it a while back, but both you and the article assume that the POST is creating a new resource. In my case, the POST is not creating a resource, but for reasons of security, caching, and query size, cannot be a GET. I'm conceptually fine with a POST as a READ operation, I just want to figure out what's the best practice here. Going by your answer, it seems like you're in the camp of extending the endpoint. I'd prefer to keep the resource as a common base (e.g. images) – Matt Feb 28 '17 at 19:57
  • To the best of my knowledge, POST carries a very specific semantics. It creates/updates a subresource. You cannot force a POST to convey a different meaning (moreover, by definition POST is not safe nor idempotent. Think twice if you are using it in a safe or idempotent way). In my opinion, your example of a rev. image search reduces to my solution: "*hey server, find me this image* = *hey give me results based on the ticket I submit*". Your constraint of not designing new resources leaves few room for properly using the POST for reading, I'm afraid you cannot be very RESTful in this way, – MaVVamaldo Feb 28 '17 at 20:51