Let's imagine you have a REST resource Restriction (think of a roadblock) that has some filters (e.g. street, direction etc.). The restriction has an expiry which is a datetime. It is only considered in the application logic, when the expiry is in the future or unset/null (no expiry).
Now with procedural style, I could just have a method on the restriction saying expire() which would set the expiry to the current time.
With REST we modify the state of resources instead. I am torn between these more or less functionally equivalent API definitions:
PATCH /restrictions/{id}
data = {
"expiry": 1558654742
}
Client explicitly sets the expiry. This bears the risk of user error with time zones, wrong host clocks etc. Also the client is not supposed to have any choice other than current time.
PATCH /restrictions/{id}
data = {
"expired": true
}
The expired field is a transient virtual property that is translated on the backend to expiry = now. This might be confusing for clients. Also the value for expired can be only true, so there's some redundancy here.
DELETE /restrictions/{id}
Resource stays persisted (soft delete), but is not returned by GET on the collection, which only returns non-expired restrictions. There is no GET on individual restrictions.
PUT /restrictions/{id}/expiry
data = {}
Creates a new virtual resource (no other methods on this path) which represents the expiry. Not sure whether PUTs without any data are idiomatic though.
Right now I do not plan on returning individual restrictions, and the list of all restrictions will return only the non-expired ones by default.
Which one of those methods would you consider the most idiomatic and obvious for a RESTful web service?
If the resource will return a 404 after expiring, DELETE is a great method for this.
Which one of those methods would you consider the most idiomatic and obvious for a RESTful web service?
If you want idiomatic REST, think about how you would do it with a web site.
You'd probably start with GET /restrictions/{id}, and in addition to data about the restriction there would be a form -- maybe embedded in the representation, maybe in the representation of another resource but available via link. You would then submit that form, which would bundle up the fields into an application/x-www-form-urlencoded docement included in a POST request. You could use any URI as the action of the form (and therefore the target-uri of the POST request), but the most useful would probably be POST /restrictions/{id}, because HTTP compliant clients will know to invalidate any previously cached representations of /restrictions/{id}.
Right now I do not plan on returning individual restrictions, and the list of all restrictions will return only the non-expired ones by default.
Same game, but instead of using an identifier for the individual restriction, you would use the uri for the-list-of-non-expired-restrictions. Your resource model doesn't have to match your data model.
There's no rule that says the content-type of a POST must be application/x-www-form-urlencoded. You could post other representations, including your own custom types if that makes things easlier (of course, you have to document the type, and the only clients that are going to send the type are those that have implemented it; the big advantage to the standard media types is that you get lots of clients "for free".)
PUT and PATCH are acceptable alternatives if modifying representations directly seems reasonable. PUT (and probably PATCH, by inference) doesn't actually require that the server accepts the requests as is:
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.
However, there is no guarantee that such a state change will be observable, since the target resource might be acted upon by other user agents in parallel, or might be subject to dynamic processing by the origin server, before any subsequent GET is received. A successful response only implies that the user agent's intent was achieved at the time of its processing by the origin server.
There are conventions to be followed in the response that distinguish "I accepted your representation as is" from other responses.
One of the few constraints REST has is the one of cacheability. The constraints, as the name implies, are, according to Fielding, not an option, even though he talked about hypermedia in this context. Though the general rule applies here as well.
Caching allows local or intermediary applications, so called caches, to store the response of a certain URI for a certain amount of time and if a further safe request for that same URI hits the cache, the cache will serve the client with its stored response rather then routing the request to the server.
In case of your expiration you need to include thoughts about the expiration of such cached values as well, otherwise caches might still serve clients with data that shouldn't exist further.
HTTP talks about caching in RFC 7234 in detail and responses may tell a cache for how long a resource should be considered fresh via either the Cache-Control header or the Expires header. If both are present the former one wins. In example a response header such as Cache-Control: max-age=3600 defines that a response should be considered fresh for up to 3600 seconds while Expires should use a date-time format specified in RFC 7231 such as Expires: Fri, 24 May 2019 05:20:00 CET.
Unfortunately though, RFC 7234 does not talk about how a client can set such a directive actively, as this is considered a server task. Cache-Control does provide some request headers, though these are more of indications that a client either will or wont accept stale data, but not directives to the server to set a respective expiration date. Usually, if a client does not want a certain resource to be further available it should DELETE the resource. If you read up on DELETE though, you might be astonished that it actually does not guarantee that the resource will ever be removed at all. All it states is, that after successfully processing such a request the mapping of the URI to the resource is removed. Whether the same resource is accessible through a different URI or not is not is a different story. "Customizing" DELETE with some kind of parameter to remove the resource after a certain amount of time might work for your API, though might not be understandable for different kind of APIs, therefore this is not recommendable in general.
By using PATCH the information on the expiration timestamp needs to be part of the resource itself. Usually such an information is considered a meta data of a resource and not the or part of the actual data. Therefore I'm not in favor for PATCH either, though this is clearly an opinionated take.
If all of the other HTTP methods do not fit the bill, POST should be taken, as here the server will process the request according to its own semantic. It may apply different heuristics upon receiving different payloads on the same endpoint as well. If you'd have to design such a feature on the Web you may have an edit page of an entry where you have the option to set an expiration date. Upon clicking the submit button of the form your browser is performing a POST request, including the expiration date, and the server will know what to do based on certain heuristics available on the server. I.e. the availability of the expiration date field in the request may instruct the server to actually queue a removal of the entry, update the expiration date meta data of the target resource and return an updated Cache-Control: max-age=... or Expires: ... header on incoming requests to also inform caches about not shipping cached responses of that resource past that time point.
Usually, unsafe operations, like POST, PUT or DELETE invalidate cached responses of a target resource by default, in a case where two users perform cacheable GET requests against a server, both having an intermediary cache in between that differs from the intermediary cache of the other user, and user 1 now expires the origin resource, there might be a case where user 2 will still get served by "his" intermediary cache for the target resource, even though the resource was already deleted on the origin server as his intermediary cache still sees the response as fresh enough and therefore serves use 2 with that stored response. The cached response of the target URI in user 1's cache should already have been removed by the initial POST request but also any eventually cacheable response might have returned an updated cache header and thus led to an expiration at the specified time point. It is therefore important to set the time values for the cache not to high into the future, but also not to short so that caching gets useless.
For resources that may be critical to get removed and not served by caches, in order to prevent the above mentioned case it is probably best to specify Cache-Control: no-cache in general so that such entries are not stored by caches further and requests are directly processed by the API/server itself.
To sum up this post, something like an expiration time point should be considered meta-data of a resource and not main-data. While DELETE may sound great at first, it does not support a removal after some time and an other API might perform such a request immediately, besides not guaranteeing to really remove that resource at all. POST, as the all-purpose toolset of HTTP, or other HTTP operations such as PUT or PATCH may be used here as well, even though the latter "work" under the premise that the body of a request belongs to the actual data of the resource. You should also consider caching into your design and use either Cache-Control: max-age=... or Expires: ... if your resource is non-critical or Cache-Control: no-cache in case of resources that must not ever (for whatever reason) return outdated information to clients. Regardless of the HTTP method you use, you should also think about how the server is allowing the client to set this option in general. Similar to the Web, a form-based solution avoids out-of-band information and thus simplifies the interaction with the API in general, as all of the information are already provided or are obtainable via further links.
Related
As an example I have an order where the invoicing address can be modified. The change might trigger various additional actions (i.e. create a cancellation invoice and a new invoice with the updated address).
As recommended by various sources (see below) I don't want to have a PATCH on the order resource, because it has many other properties, but want to expose a dedicated endpoint, also called "intent" resource or subresource according to the web links below:
/orders/{orderId}/invoicing-address
Should I use a POST or a PATCH against this subresource?
The invoicing address itself has no ID. In the domain layer it is represented as a value object that is part of the order entity.
What ETag should be used for the subresource?
The address is part of the order and together with the items they form an aggregate in the domain layer. When the aggregate is updated it gets a new version number in the database. That version number is used as an ETag for optimistic locking.
Should a GET on invoicing-address respond with the order aggregate version number or a hash value of the address DTO in the ETag header?
What payload should be returned after updating the address?
Since the resource is the invoicing address it seems natural to return the updated address object (maybe with server side added fields). Should the body also include the ID/URI and the ETag of the order resource?
None of the examples I found with subresources showed any server responses or considered optimistic locking.
https://rclayton.silvrback.com/case-against-generic-use-of-patch-and-put
https://www.thoughtworks.com/insights/blog/rest-api-design-resource-modeling
https://softwareengineering.stackexchange.com/questions/371273/design-update-properties-on-an-entity-in-a-restful-resource-based-api (see provided answer)
https://www.youtube.com/watch?v=aQVSzMV8DWc&t=188s (Jim Webber at about about 31 mins)
As far as REST is concerned, "subresources" aren't a thing. /orders/12345/invoicing-address identifies a resource. The fact that this resource has a relationship with another resource identified by /orders/12345 is irrelevant.
Thus, the invoicing-address resource should understand HTTP methods exactly the same way as every other resource on the web.
Should I use a POST or a PATCH against this subresource?
Use PUT/PATCH if you are proposing a direct change to the representation of the resource. For example, these are the HTTP methods we would use if we were trying to fix a spelling error in an HTML document (PUT if we were sending a complete copy of the HTML document; PATCH if we were sending a diff).
PUT /orders/12345/invoicing-address
Content-Type: text/plain
1060 W Addison St.
Chicago, IL
60613
On the other hand, if you are proposing an indirect change to the representation of the resource (the request shows some information to the server, and the server is expected to compute a new representation itself)... well, we don't have a standardized method that means exactly that; therefore, we use POST
POST serves many useful purposes in HTTP, including the general purpose of “this action isn’t worth standardizing.” -- Fielding, 2009
What ETag should be used for the subresource?
You should first give some thought to whether you want to use a strong-validator or a weak validator
A strong validator is representation metadata that changes value whenever a change occurs to the representation data that would be observable in the content of a 200 (OK) response to GET.
...
In contrast, a weak validator is representation metadata that might
not change for every change to the representation data.
...
a weak entity-tag ought to change whenever the origin server wants caches to invalidate old responses.
I might use a weak validator if the representation included volatile but insignificant information; I don't need clients to refresh their copy of a document because it doesn't have the latest timestamp metadata. But I probably wouldn't use an "aggregate version number" if I expected the aggregate to be changing more frequently than the invoicing-address itself changes.
What payload should be returned after updating the address?
See 200 OK.
In the case of a POST request, sending the current representation of the resource (after changes have been made to it) is nice because the response is cacheable (assuming you include the appropriate metadata signals in the response headers).
Responses to PATCH have similar rules to POST (see RFC 5789).
PUT is the odd man out, here
Responses to the PUT method are not cacheable.
Should the body also include the ID/URI and the ETag of the order resource?
Entirely up to you - HTTP components aren't going to be paying attention to the representation, so you can design that representation as makes sense to you. On the web, it's perfectly normal to return HTML documents with links to other HTML documents.
Please let me know if I misunderstanding.
Getting all active users
GET /api/users?active
What if I want to get all active user's messages
GET /api/users/active/messages
Or what if I want to delete all active user's messages
DELETE /api/users/no-active/messages
How to request REST API correctly for GET & DELETE without resources ID?
From the perspective of REST, this question doesn't make a lot of sense. Any named information can be a resource, and we use the resource identifier (aka, the URI) to identify which resource we are talking about.
GET /api/users?active
In this query, /api/users?active is a resource identifier (what RFC 7230 refers to as the request-target expressed in origin form).
Your resource, in this case is "all active users", or perhaps more precisely "the list of all active users"; the representation of that list will change over time depending on which users are currently active.
GET /api/users/active/messages
Same idea here, the resource is the list of messages.
Now normally when we are trying to modify a resource, we use the identifier of the resource as the target-uri for the change. So modifications to the list of messages would all share a common target-uri
POST /api/users/active/messages
PUT /api/users/active/messages
PATCH /api/users/active/messages
DELETE /api/users/active/messages
This is because the URI serves as a cache key, and general-purpose components that are familiar with HTTP caching semantics will know to invalidate any previously cached representations of the resource.
In HTTP, DELETE has a precise semantic meaning, which is to remove the association between the identifier and its representations. The natural consequence of a successful DELETE is that a subsequent GET would return a 404 Not Found (which means that the requested target-uri has no current representation).
If what you are intending is to modify the representation, then POST/PUT/PATCH are the more natural choices.
PUT /api/users/active/messages
Content-Type: application/json
[]
is a message that means "replace your current representation with this one".
Replacing one representation with another is pretty trivial when your implementation is just a document store - you validate the incoming representation, and then overwrite the old representation with the new. With dynamically generated representations, supporting the same semantics is a lot more work.
It may ease your life considerably to POST a "delete all messages" request to the resource, rather than trying to PUT a new representation.
Different methods can have same route:
Delete (DELETE) can still be the same:
DELETE /api/users/active/messages
Designing API using JSONAPI 1.0 standard there is no PUT method. There is only POST method for create resource and PATCH for partially update. We have use case where user can send request to the server and if resource doesn't exist then must be created otherwise updated. RFC describe such method as a PUT. Next quoting mentioned RFC 5789 standard for PATCH there is information:
"If the Request-URI does not point to an existing resource, the server MAY create a new resource,
depending on the patch document type (whether it can logically modify a null resource) and permissions, etc."
Is it good idea to have PATCH method for update and create resource? Which standard should be used to support both PUT and PATCH methods (maybe OpenApi)?
How to interpret RFC description?
Best Regards
We have use case where user can send request to the server and if resource doesn't exist then must be created otherwise updated.
The right answer, in this case, is almost certainly going to be to POST your request to the collection resource, and let the server figure out the "right" thing to do.
A resource can be created by sending a POST request to a URL that represents a collection of resources.
Using PUT to create a resource assumes that the client can/should guess what the correct identifier for the new resource should be. If we're not giving the client that authority/control, then the request needs to instead use a stable target-uri, and the server computes the side effects on other resources.
In JSON:API, the server gets to control the choice of URI for the new item.
POST /photos HTTP/1.1
Content-Type: application/vnd.api+json
...
HTTP/1.1 201 Created
Location: http://example.com/photos/550e8400-e29b-41d4-a716-446655440000
If the API were supporting PUT semantics, an equivalent change would look
something like
PUT /photos/550e8400-e29b-41d4-a716-446655440000 HTTP/1.1
Content-Type: application/vnd.api+json
HTTP/1.1 201 Created
But JSON:API has decided that PUT isn't interesting yet. Reading between the lines, the authors decided that PUT should be reserved until more implementations demonstrate that they understand the HTTP spec.
So instead you have POST to the collection for the create, and PATCH on the item to for partial or complete replacement.
That in turn implies that if the client doesn't/cannot know that a resource already exists, that it should POST to the collection. The server, in turn, should be aware that the resource may already exist, and do something sensible (replace the representation of the resource, redirect the client to the resource, etc). How the server achieves that would be an implementation detail.
Looking into Internet deal with REST HTTP methods I have never seen that PATCH can be used for resource creation therefore I am surprised that JsonApi forgo PUT method.
PATCH can certainly be used for resource creation -- see RFC 5789
If the Request-URI does not point to an existing resource, the server MAY create a new resource, depending on the patch document type (whether it can logically modify a null resource) and permissions, etc.
It's an uncommon choice, because PUT semantics are a better fit for that use case. Choosing to support PATCH, but not PUT, is weird.
I am surprised that JsonApi forgo PUT method
I am also surprised.
It might be possible to resolve your concerns by registering a new profile, encouraging the community to adopt a common pattern for the semantics that you need.
According to Postel's law one should be conservative in what you do, be liberal in what you accept from others.
Two common media-types used with PATCH are application/json-patch+json (a.k.a. JSON Patch) and application/json-merge-patch+json (a.k.a MergePatch).
MergePatch defines a couple of rules that determine whether a part needs to be added, removed or updated. The spec defines that a request received of that type needs to be processed by calling a function that takes in two arguments, the target resource and the representation received. The target itself might be either a JSON value or undefined. If the resource does not yet exist it is undefined and will lead to all of the values in the received patch document to be added to the yet undefined resource. This is basically your resource creation then.
JSON Patch, in contrast to MergePatch, is specified to only operate on JSON documents. There is no mention how the patch needs to be applied in case no resource was yet available. This makes somehow sense if you look at the operations is offers, such as test, remove, replace or move that only work if there is a counterpart in the original JSON document availalbe. This media-type is quite close to the actual PATCH definition in that a client sends a set of instructions, which were previously calculate by the client, that need to be applied atomically by the server. Either all or none of the changes are applied. Here a client should have already fetched the current state of the target resource beforehand, otherwise it wont be able to calculate the necessary changes to transform the current representation into the desired one. So applying a representation of that media-type only makes sense if there is already a resource available. If the client saw that no target resource is yet available it simply can use POST then to create the resource. If a client though sends a patch document containing only add operations I'd create a JSON representation and add all of the fields accordingly though.
As you see there are two different takes on how PATCHing can be done in HTTP. One being very close to the original idea of how patching is done in software engineering for decades while the other method being a more pragmatic approch to partial updating remote resources. Which one you use or support (in best case both) is your choice.
I need to delete multiple items by id in the batch however HTTP DELETE does not support a body payload.
Work around options:
1. #DELETE /path/abc?itemId=1&itemId=2&itemId=3 on the server side it will be parsed as List of ids and DELETE operation will be performed on each item.
2. #POST /path/abc including JSON payload containing all ids. { ids: [1, 2, 3] }
How bad this is and which option is preferable? Any alternatives?
Update: Please note that performance is a key here, it is not an option execute delete operation for each individual id.
Along the years, many people fell in doubt about it, as we can see in the related questions here aside. It seems that the accepted answers ranges from "for sure do it" to "its clearly mistreating the protocol". Since many questions was sent years ago, let's dig into the HTTP 1.1 specification from June 2014 (RFC 7231), for better understanding of what's clearly discouraged or not.
The first proposed workaround:
First, about resources and the URI itself on Section 2:
The target of an HTTP request is called a "resource". HTTP does not limit the nature of a resource; it merely defines an interface that might be used to interact with resources. Each resource is identified by a Uniform Resource Identifier (URI).
Based on it, some may argue that since HTTP does not limite the nature of a resource, a URI containing more than one id would be possible. I personally believe it's a matter of interpretation here.
About your first proposed workaround (DELETE '/path/abc?itemId=1&itemId=2&itemId=3') we can conclude that it's something discouraged if you think about a resource as a single document in your entity collection while being good to go if you think about a resource as the entity collection itself.
The second proposed workaround:
About your second proposed workaround (POST '/path/abc' with body: { ids: [1, 2, 3] }), using POST method for deletion could be misleading. The section Section 4.3.3 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; Posting a message to a bulletin board, newsgroup, mailing list, blog, or similar group of articles; Creating a new resource that has yet to be identified by the origin server; and Appending data to a resource's existing representation(s).
While there's some space for interpretation about "among others" functions for POST, it clearly conflicts with the fact that we have the method DELETE for resources removal, as we can see in Section 4.1:
The DELETE method removes all current representations of the target resource.
So I personally strongly discourage the use of POST to delete resources.
An alternative workaround:
Inspired on your second workaround, we'd suggest one more:
DELETE '/path/abc' with body: { ids: [1, 2, 3] }
It's almost the same as proposed in the workaround two but instead using the correct HTTP method for deletion. Here, we arrive to the confusion about using an entity body in a DELETE request. There are many people out there stating that it isn't valid, but let's stick with the Section 4.3.5 of the specification:
A payload within a DELETE request message has no defined semantics; sending a payload body on a DELETE request might cause some existing implementations to reject the request.
So, we can conclude that the specification doesn't prevent DELETE from having a body payload. Unfortunately some existing implementations could reject the request... But how is this affecting us today?
It's hard to be 100% sure, but a modern request made with fetch just doesn't allow body for GET and HEAD. It's what the Fetch Standard states at Section 5.3 on Item 34:
If either body exists and is non-null or inputBody is non-null, and request’s method is GET or HEAD, then throw a TypeError.
And we can confirm it's implemented in the same way for the fetch pollyfill at line 342.
Final thoughts:
Since the alternative workaround with DELETE and a body payload is let viable by the HTTP specification and is supported by all modern browsers with fetch and since IE10 with the polyfill, I recommend this way to do batch deletes in a valid and full working way.
It's important to understand that the HTTP methods operate in the domain of "transferring documents across a network", and not in your own custom domain.
Your resource model is not your domain model is not your data model.
Alternative spelling: the REST API is a facade to make your domain look like a web site.
Behind the facade, the implementation can do what it likes, subject to the consideration that if the implementation does not comply with the semantics described by the messages, then it (and not the client) are responsible for any damages caused by the discrepancy.
DELETE /path/abc?itemId=1&itemId=2&itemId=3
So that HTTP request says specifically "Apply the delete semantics to the document described by /path/abc?itemId=1&itemId=2&itemId=3". The fact that this document is a composite of three different items in your durable store, that each need to be removed independently, is an implementation details. Part of the point of REST is that clients are insulated from precisely this sort of knowledge.
However, and I feel like this is where many people get lost, the metadata returned by the response to that delete request tells the client nothing about resources with different identifiers.
As far as the client is concerned, /path/abc is a distinct identifier from /path/abc?itemId=1&itemId=2&itemId=3. So if the client did a GET of /path/abc, and received a representation that includes itemIds 1, 2, 3; and then submits the delete you describe, it will still have within its own cache the representation that includes /path/abc after the delete succeeds.
This may, or may not, be what you want. If you are doing REST (via HTTP), it's the sort of thing you ought to be thinking about in your design.
POST /path/abc
some-useful-payload
This method tells the client that we are making some (possibly unsafe) change to /path/abc, and if it succeeds then the previous representation needs to be invalidated. The client should repeat its earlier GET /path/abc request to refresh its prior representation rather than using any earlier invalidated copy.
But as before, it doesn't affect the cached copies of other resources
/path/abc/1
/path/abc/2
/path/abc/3
All of these are still going to be sitting there in the cache, even though they have been "deleted".
To be completely fair, a lot of people don't care, because they aren't thinking about clients caching the data they get from the web server. And you can add metadata to the responses sent by the web server to communicate to the client (and intermediate components) that the representations don't support caching, or that the results can be cached but they must be revalidated with each use.
Again: Your resource model is not your domain model is not your data model. A REST API is a different way of thinking about what's going on, and the REST architectural style is tuned to solve a particular problem, and therefore may not be a good fit for the simpler problem you are trying to solve.
That doesn’t mean that I think everyone should design their own systems according to the REST architectural style. REST is intended for long-lived network-based applications that span multiple organizations. If you don’t see a need for the constraints, then don’t use them. That’s fine with me as long as you don’t call the result a REST API. I have no problem with systems that are true to their own architectural style. -- Fielding, 2008
Why would you set ETags to a "MUST requirement level"?
You obtains the resource before the ETags returned...
I'm working on a project where I am the client that sends HTTP requests to a server that returns an HTTP Cache-Control header with ETags to cache response (where in each addition request it gets compared to the If-None-Match header to determine if the data is stale and if a new request should be made). In my current project the ETags parameter is using the conditional GET architecture with the MUST requirement level as specified in RFC 2119.
MUST This word, or the terms "REQUIRED" or "SHALL", mean that the definition is an absolute requirement of the specification.
I don't understand the intent of using a conditional GETwith the MUST requirement level? From my understanding the MUST requirement is there to limit (is that right?) the resources provided to the client that makes the request, however the client (me in this case) already has the resources from the first request. Where I can continue obtaining the same resource (or a fresher resource if it gets updated) as much as I want with or without returning the If-None-Match and ETag header fields.
What would be the purpose of setting it to the MUST requirement level in this case if it's not limiting the resources returned, Aside from being able to cache and limiting the amount of requests to the server (Im asking from the client point of view, yes I know I can cache it but why the MUST requirement)? Isn't this only used for limiting resources?
So basically, doesn't it make this MUST requirement not a requirement if I can obtain the resources with or without it? Am I missing something here?
My Question is not asking the what and how Etags, Cache-Control, or If-None-Match headers work.
Thanks in advance, cheers!
Why would ETags set to a MUST requirement if you already have the resource?
A client MUST use a conditional GET to reduce the data traffic.
Aside from being able to cache and limiting the amount of requests to the server
The number of requests stays the same, but the total number of data transferred changes.
Using ETags in if-none-matched GET requests (conditional GET)
When you make a API call, the response header includes an ETag with a value that is the hash of the data returned in the API call. You store this ETag value for use in the next request.
The next time you make the same API call, you include the If-None-Match request header with the ETag value stored from the first step.
If the data has not changed, the response status code will be 304 – Not Modified and no data is returned.
If the data has changed since the last query, the data is returned as usual with a new ETag. The game starts again: you store the new ETag value and use it for subsequent requests.
Why?
The main reason for using conditional GET requests is to reduce data traffic.
Isn't this only used for limiting resources?
No...
You can ask an API for multiple resources in one request.
(Ok, thats also limiting resources by saving the other requests.)
You can prevent a method (e.g. PUT) from modifying an existing resource, when the client believes that the resource does not exist (replace protection).
I can obtain the resources with or without it?
When you ignore the "MUST use conditional GET" then (a) the traffic will increase and (b) you lose the "resource has changed" indication coming from server-side. You would have to implement the comparison handling on client side: is the resource of the second request newer than the one from the first request.
I found my question wasn't asking the "right question" due to me merging my understand of other headers (thanks to #dcerecedo's comment to get my pointed in the right direction) that were affecting my understand of why MUST was being used.
The MUST was more relivent to other headers, in my case private, max-age=3600 and must-revalidate
Where
Cache-Control: private restricts proxy servers from caching it, this helps you keep your data off a server you dont trust and prevents a proxy from caching user specific data that’s not relevant to everyone (like a user profile).
Cache-Control "max-age=3600, must-revalidate" tell both client caches and proxy caches that once the content is stale (older than 3600 seconds) they must revalidate at the origin server before they can serve the content. This should be the default behavior of caching systems, but the must-revalidate directive makes this requirement unambiguous.
Where after the max-age expires the client should revalidate. It might revalidate using the If-Match or If-None-Match headers with an ETag, or it might use the If-Modified-Since or If-Unmodified-Since headers with a date. So, after expiration the browser will check at the server if the file is updated. If not, the server will respond with a 304 Not Modified header and nothing is downloaded.