1

I have inherited a REST endpoint that has one key action it performs, named find. This action will generally just query for any matching resources, rank them, and return the most favorable result. However, in some cases, the payload specifies a flag indicating we should be upsert the record if no match was found. The initial build used POST, which seems wrong to me. My inclination was that find should be related to a GET call.


Upon further reading, it appears that GET should never modify server resources.

Requests using GET should only be used to request data (they shouldn't include data).

Also, there is currently a payload in the request body, and apparently that too is frowned upon.

It is better to just avoid sending payloads in GET requests.

So maybe GET is wrong, but POST still seems wrong also. It seems to me that PUT would be more appropriate.

The HTTP PUT request method creates a new resource or replaces a representation of the target resource with the request payload.

The difference between PUT and POST is that PUT is idempotent: calling it once or several times successively has the same effect (that is no side effect), whereas successive identical POST requests may have additional effects, akin to placing an order several times.

I do believe this request would be idempotent. If you sent the same payload multiple times, the first request would create the missing resource, and subsequent requests would find it without modifying server state.


So it still seems weird to me that in the case we know we only run a query, we use anything other than GET. But requiring the caller to change their HTTP method rather than setting some flag in the request body is apparently cumbersome. I guess the question is, how much stock should I place in using the "most correct" HTTP method, and is that method in fact PUT?

1 Answer 1

2

I see a couple of layers worth of revision options here.

At the highest level, I don't know that this API design is coherent. Including a flag inside the payload that changes the action between two or three different plausible HTTP verbs? If you have the option to do so, I would break that into two separate endpoints. One of them would do the upsert, and the other would only retrieve.

I think the endpoint is also mis-named: find definitely doesn't cover what it does, and can easily lead to misinterpretation by API consumers.


That doesn't solve, however, the question of what HTTP verb the upsert-y action should be, whether it's part of a single big endpoint or a separate one. I believe the correct answer is POST.

The HTTP RFC 9110 says about POST:

The POST method requests that the target resource process the representation enclosed in the request according to the resource's own specific semantics. For example, POST is used for the following functions (among others): Providing a block of data, such as the fields entered into an HTML form, to a data-handling process;

...

If one or more resources has been created on the origin server as a result of successfully processing a POST request, the origin server SHOULD send a 201 (Created) response containing a Location header field that provides an identifier for the primary resource created (Section 10.2.2) and a representation that describes the status of the request while referring to the new resource(s).

By this rubric, your service could plausibly accept a POST and return an HTTP 201 to indicate that it did in fact create a resource, or an HTTP 200 to indicate it is returning existing data. The fact that your service is idempotent doesn't particularly bear either way. The action of the service falls neatly into at least one, and arguable several, of the example POST actions.


The PUT resource, meanwhile, is described:

The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message content. A successful PUT of a given representation would suggest that a subsequent GET on that same target resource will result in an equivalent representation being sent in a 200 (OK) response

Your API sounds like it meets this contract, but I think there's a semantic mismatch. A PUT requests that the "state of the target resource" be applied from the request.

But you're not applying against the state of the target resource, find. find is performing some kind of data processing against other resources that are not named in the URL but specified by some combination of payload parameters. Those parameters, if I read the question right, may or may not uniquely identify any specific resource. For example, if one of the other records in the find result set were modified such that it became a stronger match for the query, the results of a GET to the same resource would change.

I read this as falling clearly into the semantic of POST:

the target resource process the representation enclosed in the request according to the resource's own specific semantics...

If the parameters made up a unique key - like

/my-api/find/external-id/1111 

I could get closer to seeing the case for PUT. But because your API in fact is performing a search and returning the top result, but not necessarily a single record uniquely identified by that URI, I don't think it fits the rubric for a PUT.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.