API: use HTTP headers to control business flow - rest

I've just heard that using HTTP headers to control business flow is bad practice. Could someone give me an explanation why?
In my case, the default response contains only simple information but if you add X-Full-Info header there will be more information but request processing will take more time. Why is it bad practice? I was suggested to use query parameter.

One reason is caching. Assuming you are using GET, the result may be cached, and your http header will be ignored if the result is served from a cache. (Either a remote cache or your browser’s cache)
But if you use a query parameter to choose the results, the cache will know which result to return, and won’t return the wrong result.
You can try to solve this using the “Vary” header, but support for that header means more work on your part, and support for that header is not as widespread: https://blogs.msdn.microsoft.com/ieinternals/2009/06/17/vary-with-care/

Related

REST api: how bad idea is it to pass a new value as a url query string in PATCH method?

I had to create the implementation of a PATCH method, because of the need to update only a single value.
I spend too much time wandering is there any way to only pass the single value, instead of a whole request body.
And I've got the feeling that it is bad idea to pass it as a url query param, but why? Can someone explain if and why it is a bad idea, for example, to try and update the age of a customer like this:
PATCH host/api/customers/{id}?newAge=45
And I've got the feeling that it is bad idea to pass it as a url query param
Yes, it is.
but why?
Because the target-uri in the request identifies the document (resource) that we are taking about, not the change that we want to make to that document.
In other words, as far as a general purpose component is concerned, in the same way that
/host/api/customers/{id}
/host/api/customers/{id}/newAge/45
identify different resources, so too do
/host/api/customers/{id}
/host/api/customers/{id}?newAge=45
In other words, both the path and the query part are part of the "identifier", see RFC 7230, RFC 3986.
RFC 7234 describes one setting where your proposal is likely to cause a problem: cache-invalidation. Because the target-uri is the primary cache key, a general purpose cache will not recognize that
GET /host/api/customers/{id}
PATCH /host/api/customers/{id}?newAge=45
are two requests about the same object, and that the copy of the document we cached after the first request is no longer valid after the second succeeds.
If we were modifying this resource via forms on the web, we would use a request like
POST /host/api/customers/{id} HTTP/2.0
Content-Type: application/x-www-form-urlencoded
newValue=45
And because the identifiers match, general purpose caches can do the right thing by just following the standard.
Update 2022-07-11: the current standard for cache invalidation is RFC-9111

What is the correct REST method for performing server side validation?

If I don't want to update a resource, but I just want to check if something is valid (in my case, a SQL query), what's the correct REST method?
I'm not GETting a resource (yet). I'm not PUTting, POSTing, or PATCHing anything (yet). I'm simply sending part of a form back for validation that only the server can do. Another equivalent would be checking that a password conforms to complexity requirements that can only be known by the domain, or perhaps there are other use cases.
Send object, validate, return response, continue with form. Using REST. Any ideas? Am I missing something?
What is the correct REST method for performing server side validation?
Asking whether a representation is valid should have no side effects on the server; therefore it should be safe.
If the representation that you want to validate can be expressed within the URI, then, you should prefer to use GET, as it is the simplest choice, and gives you the best semantics for caching the answer. For example, if we were trying to use a web site to create a validation api for a text (and XML or JSON validator, for instance), then we would probably have a form with a text area control, and construct the identifier that we need by processing the form input.
If the representation that you want to validate cannot be expressed within the URI, then you are going to need to put it into the message body.
Of the methods defined by RFC 7231, only POST is suitable.
Additional methods, outside the scope of this specification, have been standardized for use in HTTP. All such methods ought to be registered within the "Hypertext Transfer Protocol (HTTP) Method Registry" maintained by IANA, as defined in Section 8.1.
The HTTP method registry gives you a lot of options. For this case, I wouldn't bother with them unless you find either a perfect match, or a safe method that accepts a body and is close enough.
So maybe REPORT, which is defined in RFC 3253; I tend to steer clear of WebDAV methods, as I'm not comfortable stretching specifications for "remote Web content authoring operations" outside of their remit.
TLDR; There's a duplicate question around the topic of creating validation endpoints via REST:
In your case a GET request would seem sufficient.
The HTTP GET method is used to read (or retrieve) a representation of a resource. In the “happy” (or non-error) path, GET returns a representation in XML or JSON and an HTTP response code of 200 (OK). In an error case, it most often returns a 404 (NOT FOUND) or 400 (BAD REQUEST).
restapitutorial.com
For validating your SQL query you could use a GET request to get the valid state of your query potentially using a query parameter to achieve this.
GET: api/validateQuery?query="SELECT * FROM TABLE"
Returning:
200 (OK): Valid Query
400 (MALFORMED): Invalid Query
404 (NOT FOUND): Query valid but returns no results (if you plan on executing the query)
I think this type of endpoint is best served as a POST request. As defined in the spec, POST requests can be used for
Providing a block of data, such as the fields entered into an HTML form, to a data-handling process
The use of GET as suggested in another post, for me, is misleading and impractical based on the complexity & arbitrarity of SQL queries.

Search verb in HTTP API

What is best practice for search in API?
GET + query parameters, example: GET /search?q=phone
GET + parameters in body, example: GET /search {"query": "phone"}
POST + parameters in body, example: POST /search {"query": "phone"}
Don't include a body with a GET request. That's against the spec:
A payload within a GET request message has no defined semantics;
sending a payload body on a GET request might cause some existing
implementations to reject the request.
There are tradeoffs between the other two options. GET requests are cacheable, safe, and idempotent. They're also limited in size.
POST requests are not reliably cacheable, safe, or idempotent, and have no size limit. There's also more flexibility baked in - you can later create a filter resource on the server side in addition to returning the search result, and later searches can use that filter, possibly with a GET, although be careful if you allow caching and changes to the filter definition after it's created.
Looking at your specific example, supporting a single "search" endpoint can get messy pretty fast. If you haven't already, I would encourage you to consider other options.
POST requests are considered to change or create data on the server. GET is considered as a "Safe Method" which have no effect on the server database.
Since search requests do normally not change any data you should use a GET request. The limit is at least 2000 symbols (IE) so most of the times you are pretty safe.
Definitely do 1, GET using query parameters. It is much more likely to be cached.
If nothing in the data model changes on the server, your request should be GET. Server ops like logging is OK, but creation of a filter (as another answer suggested), as distinct from a query cache, say, is not.

Best practice for file-based search in rest service

I'm helping build a similarity search service for files. One way to search for something is with a GET request, by giving a file's URL, but I also need to allow clients to send the file directly. I have to following options:
Make the client send a GET request with a Payload; it seems this is not recommended -- HTTP GET with request body
Use something else than GET (maybe a PUT?) for file-based search. The problem is none of the other HTTP methods seems to suit this purpose.
What option would suit best here? I'm not an expert in this field, and I can't figure out what's the right thing to do in this situation.
Here is the rule I have always followed with REST.
GET - only querying data and returning a data set.
POST - Creating data in the database
PUT - Modifying data
DELETE - Destroy data in the database.
If you are sending a payload for search params, you can do a GET and put those params (assuming they are name/value pairs) in the query string of the URI.
i.e. http://my.simsearch.com?param1=first&param2=second ...
If you are actually going to change the database then a POST or PUT is in order.
I hope this helps.
I ended up sending the payload with a GET request. Even though it's not really recommended, hopefully no libraries will complain about this.

Proper RESTful way to handle a request that is not really creating or getting something?

I am writing a little app that does one thing only: takes some user-provided data, does some analysis on it, and returns a "tag" for that data. I am thinking that the client should either GET or POST their request to /getTag in order to get a response back.
Nothing is stored on the server when the client does this, so it feels weird to use a POST. However, there is not a uniform URI for the analysis either, so using a GET feels weird, since it will return different things depending on what data is provided.
What is the best way to represent this functionality with REST?
The "best way" is to do whatever is most appropriate for your application and its needs. Not knowing that, here are a few ideas:
GET is the most appropriate verb since you're not creating or storing anything on the server, just retrieving something that the server provides.
Don't put the word get in the URI as you've suggested. Verbs like that are already provided by HTTP, so just use /tag and GET it instead.
You should use a well-understood (or "cool") URI for this resource and pass the data as query parameters. I wouldn't worry about it feeling weird (see this question's answers to find out why).
To sum up, just GET on /tag?foo=bar&beef=dead, and you're done.
POST can represent performing an action. The action doesn't have to be a database action.
What you have really created is a Remote Procedure. RPC is usually all POST. I don't think this is a good fit for REST, but that doesn't have to stop you from using simple URLs and JSON.
It seems to me like there would probably be a reason you or the user who generated the original data would want the generated tag to persist, wouldn't they?
If that's a possibility, then I'd write it as POST /tags and pass the /tags/:id resource URI back as a Location: header.
If I really didn't care about persisting the generated tag, I'd think about what the "user-generated data" was and how much processing is happening behind the scenes. If the "tag" is different enough from whatever data is being passed into the system, GET /tag might be really confusing for an API consumer.
I'll second Brian's answer: use a GET. If the same input parameters return the same output, and you're not really creating anything, it's an idempotent action and thus perfectly suited for a GET.
You can use GET and POST either:
GET /tag?data="..." -> 200, tag
The GET method means retrieve whatever information (in the form of an
entity) is identified by the Request-URI. If the Request-URI refers to
a data-producing process, it is the produced data which shall be
returned as the entity in the response and not the source text of the
process, unless that text happens to be the output of the process.
POST /tag {data: "..."} -> 200, tag
The action performed by the POST method might not result in a resource
that can be identified by a URI. In this case, either 200 (OK) or 204
(No Content) is the appropriate response status, depending on whether
or not the response includes an entity that describes the result.
according to the HTTP standard / method definitions section.
I would use GET if I were you (and POST only if you want to send files).