REST API allow update of resource depending on state of resource - rest

I have recently read the guide on implementing RESTful API's in Spring Boot from the official Spring.io tutorials website (link to tutorial: https://spring.io/guides/tutorials/rest/)
However, something in the guide seemed to contradict my understanding of how REST API's should be built. I am now wondering if my understanding is wrong or if the guide is not of as high a quality as I expected it to be.
My problem is with this implementation of a PUT method to update the status of an order:
#PutMapping("/orders/{id}/complete")
ResponseEntity<?> complete(#PathVariable Long id) {
Order order = orderRepository.findById(id) //
.orElseThrow(() -> new OrderNotFoundException(id));
if (order.getStatus() == Status.IN_PROGRESS) {
order.setStatus(Status.COMPLETED);
return ResponseEntity.ok(assembler.toModel(orderRepository.save(order)));
}
return ResponseEntity //
.status(HttpStatus.METHOD_NOT_ALLOWED) //
.header(HttpHeaders.CONTENT_TYPE, MediaTypes.HTTP_PROBLEM_DETAILS_JSON_VALUE) //
.body(Problem.create() //
.withTitle("Method not allowed") //
.withDetail("You can't complete an order that is in the " + order.getStatus() + " status"));
}
From what I read at https://restfulapi.net/rest-put-vs-post/ a PUT method should be idempotent; meaning that you should be able to call it multiple times in a row without it causing problems. However, in this implementation only the first PUT request would have an effect and all further PUT requests to the same resource would result in an error message.
Is this okay according to RESTful API's? If not, what would be a better method to use? I don't think POST would be any better.
Also, in the same guide, they use the DELETE method in a similar way to change the status of an order to cancelled:
#DeleteMapping("/orders/{id}/cancel")
ResponseEntity<?> cancel(#PathVariable Long id) {
Order order = orderRepository.findById(id) //
.orElseThrow(() -> new OrderNotFoundException(id));
if (order.getStatus() == Status.IN_PROGRESS) {
order.setStatus(Status.CANCELLED);
return ResponseEntity.ok(assembler.toModel(orderRepository.save(order)));
}
return ResponseEntity //
.status(HttpStatus.METHOD_NOT_ALLOWED) //
.header(HttpHeaders.CONTENT_TYPE, MediaTypes.HTTP_PROBLEM_DETAILS_JSON_VALUE) //
.body(Problem.create() //
.withTitle("Method not allowed") //
.withDetail("You can't cancel an order that is in the " + order.getStatus() + " status"));
}
This looks very wrong to me. We are not deleting anything here, it is basically the same as the previous PUT method just with a different state we want to move to. Am I correct to assume that this part of the tutorial is bogus?
TL;DR: what HTTP method is right to use when you want to advance the status of a resource to the next stage without giving an option of going back to an earlier stage? Basically an update/patch that will invalidate its own pre-conditions.

something in the guide seemed to contradict my understanding of how REST API's should be built. I am now wondering if my understanding is wrong or if the guide is not of as high a quality as I expected it to be.
I wouldn't consider this guide to be a reliable authority - the described resource model has some very questionable choices.
From what I read at https://restfulapi.net/rest-put-vs-post/ a PUT method should be idempotent; meaning that you should be able to call it multiple times in a row without it causing problems. However, in this implementation only the first PUT request would have an effect and all further PUT requests to the same resource would result in an error message.
The authoritative definition of idempotent semantics in HTTP is currently RFC 7231.
A request method is considered "idempotent" if the intended effect on the server of multiple identical requests with that method is the same as the effect for a single such request.
Note: "effect", not "response".
PUT /orders/12345/complete
means "please replace the current representation of /orders/12345/complete with the representation in the payload". In other words "save this file on top of your current copy". Saving the same file two or three times in row produces the same effect as saving the file once, so that's "idempotent".
HTTP does not define exactly how a PUT method affects the state of an origin server beyond what can be expressed by the intent of the user agent request and the semantics of the origin server response. It does not define what a resource might be, in any sense of that word, beyond the interface provided via HTTP. It does not define how resource state is "stored", nor how such storage might change as a result of a change in resource state, nor how the origin server translates resource state into representations. Generally speaking, all implementation details behind the resource interface are intentionally hidden by the server. -- RFC 7231
So in their CURL example
PUT /orders/4/complete HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.54.0
Accept: */*
The meaning of this message is "replace the current representation of /orders/4/complete with an empty representation". But the origin server gets to choose how to do that, and which standardized responses to return to the client.
So this is fine.
All work is transacted by politely placing documents in in-trays, and then some side effect of placing that document in an in-tray causes some business activity to occur -- Jim Webber, 2011.
In this case, the document we are putting into the "in-tray" happens to be blank.
#DeleteMapping("/orders/{id}/cancel")
I would never approve that choice in a code review. DELETE (like PUT) has semantics in the "transfer of documents over a network domain".
The DELETE method requests that the origin server remove the association between the target resource and its current functionality. In effect, this method is similar to the rm command in UNIX: it expresses a deletion operation on the URI mapping of the origin server rather than an expectation that the previously associated information be deleted.
Trying to hijack the method because the spelling is kind of like the domain action is the wrong heuristic to use in choosing methods.
Relatively few resources allow the DELETE method -- its primary use is for remote authoring environments, where the user has some direction regarding its effect.
The point being that we have a general purpose document manipulation interface, and we are using that interface as a facade that allows us to drive business activity. So we should be using our standardized message semantics the same way every other page on the web does.
#PutMapping would be defensible, using the same justification as we did for /complete.
what HTTP method is right to use when you want to advance the status of a resource to the next stage without giving an option of going back to an earlier stage? Basically an update/patch that will invalidate its own pre-conditions.
PUT, PATCH, and POST are all appropriate methods to use when editing the representation of a resource. Use PUT or PATCH when you are sending a replacement representation for the resource, use POST when you are asking the server to calculate what the edit to the representation should be.

Related

which type of request is used for the `delete` button in the REST context?

I am creating a REST API for the Order screen. I have methods:
GET /api/orders
GET /api/orders/{orderId}
I have some buttons on the Order page and I created few endpoints for that:
PATCH /api/order/buttons/mark-as-read
PATCH /api/order/buttons/change-status
Now I need to add the delete button. But I don't understand how to do that. I have 2 options:
DELETE /api/orders/{orderId} - but I should send 2 additional parameters in this request
PATCH /api/order/buttons/delete - I can send my DTO in the body, but it is not a REST approach.
I want to understand which type of request is used for the delete button in the REST context?
PATCH /api/order/buttons/mark-as-read
PATCH /api/order/buttons/change-status
These are a bit strange. PATCH is a method with remote authoring semantics; it implies that you are making a change to the resource identified by the effective target URI.
But that doesn't seem to be the case here; if you are expecting to apply the changes to the document identified by /api/orders/{orderId}, then that should be the target URI, not some other resource.
PATCH /api/orders/1
Content-Type: text/plain
Please mark this order as read.
PATCH /api/orders/1
Content-Type: text/plain
Please change the status of this order to FULFILLED
Of course, we don't normally use "text/plain" and statements that require a human being to interpret, but instead use a patch document format (example: application/json-patch+json) that a machine can be taught to interpret.
I want to understand which type of request is used for the delete button in the REST context?
If the semantics of "delete" belong to the Orders domain (for instance, if it is a button that signals a desire to cancel an order) then you should be using PUT or PATCH (if you are communicating by passing updated representations of the resource) or POST (if you are sending instructions that the server will interpret).
The heuristic to consider: how would you do this on a plain HTML page? Presumably you would have a "cancel my order" form, with input controls to collect information from the user, and possibly some hidden fields. When the user submits the form, the browser would use the form data and HTML's form processing rules to create an application/x-www-form-urlencoded representation of the information, and would then POST that information to the resource identified by the form action.
The form action could be anything; you could use /api/orders/1/cancel, analogous to your mark-as-read and change-status design; but if you can use the identifier of the order (which is to say, the resource that you are changing), then you get the advantages of standardized cache invalidation for free.
It's normal for a single message handler, which has a single responsibility in the transfer of documents over a network domain, ex POST /api/orders/{orderId}, to interpret the payload and select one of multiple handlers (change-status, mark-as-read, cancel) in your domain.
you offer to use something like this: PATCH /api/orders/{orderId} and OrderUpdatesDto as JSON string in the request body?
Sort of.
There are three dials here: which effective request URI to use, which payload to use, which method to use.
Because I would want to take advantage of cache invalidation, I'm going to look for designs that use: /api/order/{orderId} as the effective request URI, because that's the URI for the responses that I want to invalidate.
It's fine to use something like a JSON representation of an OrderUpdate message/command/DTO as the payload of the request. But that's not really a good match for remote authoring. So instead of PATCH, I would use POST
POST /api/orders/1 HTTP/1.1
Content-Type: application/prs.pavel-orderupdate+json
{...}
But you can instead decide to support a remote authoring interface, meaning that the client just edits their local copy of /api/order/1 and then tells you what changes they made.
That's the case where both PUT (send back the entire document) and PATCH (send back a bunch of edits) can make sense. If GET /api/orders/1 returns a JSON document, then I'm going to look into whether or not I can support one of the general purpose JSON patch document formats; JSON Patch or JSON Merge Patch or something along those lines.
Of course, it can be really hard to get from "changes to a document" to a message that will be meaningful to a non-anemic domain. There are reasons that we might prefer supporting a task based experience, but sending a task centric DTO is not a good fit for PUT/PATCH if you also want caching to work the way I've described above.

What is the "Restful" way to command a server?

I have a REST endpoint to create an application configuration as such
POST /applications
With a body
{
"appName" : "my-new-app"
}
I returns a newly created application configuration:
{
"appName": "my-new-app",
"appId": "2ed17ff700664dad9bb32e400d39dc68",
"apiKey": "$2a$10$XVDH9F3Ix4lx2LdxeJ4ZOe7H.bw/Me5qAmaIGF.95lUgkerfTG7NW",
"masterKey": "$2a$10$XVDH9F3Ix4lx2LdxeJ4ZOeSZLR1hVSXk2We/DqQahyOFFY6nOfbHS",
"dateCreated": "2021-03-28T11:00:07.340+00:00",
"dateUpdated": "2021-03-28T11:00:07.340+00:00"
}
Note: The keys are auto-generated in the server and not passed from the client.
My question here is, what's the RESTful way to command the server to reset the keys for example:
PUT /applications/my-new-app/update_keys is not noun-based and thus, not restful, also passing a command as query parameter does not also seem to be restful since this is not a GET method rather it's a PUT (update) method.
Here's one way to send a command that is as much as possible RESTful:
Endpoint:
POST /application/:appName/actions
Example Payload:
{
"actions" : [
{
"action" : "name_of_command",
"arguments" : {
"arg1" : "param1"
}
},
{
"action" : "reset_keys",
"arguments" : {
}
}
]
}
Actions would be nouns that are part of the endpoint, and the server will process actions that are submitted (or posted) within the endpoint. And an array of actions would be best suited to allow multiple actions to be sent. And each action having arguments would also be desirable for future actions that would need arguments.
what's the RESTful way to command the server to reset the keys for example:
How would you do it with a web site?
You would be looking at some web page like /www/applications/my-new-app; within the data or the metadata you would find a link. Following that link would bring you to a form; the form would have input controls describing what fields you need to provide to send the message, in addition to any "hidden" inputs. When you click the submit button, your user agent would collect your inputs, construct from them the appropriate message body, then use the form metadata to determine what request method and uri to use.
The client never has to guess what URI to use, because the server is providing links to guide the way.
Hypertext is at the heart of the uniform interface
REST is defined by four interface constraints: identification of resources; manipulation of resources through representations; self-descriptive messages; and, hypermedia as the engine of application state.
Because the server is providing the URI for each of the links, you've got some freedom ot choose which resource "handles" which message.
One interesting way to resolve this to look at HTTP's rules for cache invalidation. The short version is that successful unsafe requests (PATCH/POST/PUT) invalidate the representations of the target-uri.
In other words, we take advantage of cache-invalidation by sending the command to the resource that we are trying to change.
So, assuming that retrieving the representation of the app occurred via a request like:
GET /applications/my-new-app HTTP/x.y
Then we would ask the server to change that resource by sending a request with that same target-uri. Something analogous to:
POST /applications/my-new-app HTTP/x.y
Content-Type: text/plain
Please rotate the keys
Form submissions on the web are usually a representation of key/value pairs, so a more likely spelling would be:
POST /applications/my-new-app HTTP/x.y
Content-Type: applications/x-www-form-urlencoded
action=Please%20rotate%20the%20keys
Your form that describes this request my have an "action" input control, that accepts text from the client, or more likely in this case action would be a hidden control with a pre-defined value.
Note: if we have multiple actions that should invalidate the /applications/my-new-app representations, we would probably use POST for all of them, and resolve the ambiguity at the server based on the request-body (if our routing framework gives us the degree of control we need, we can use that - but more common would be to have a single POST handler for each Content-Type, and parse the request body "by hand".
POST serves many useful purposes in HTTP, including the general purpose of “this action isn’t worth standardizing.” -- Fielding 2009
PUT /applications/my-new-app/update_keys is not noun-based and thus, not restful,
That's not true: REST doesn't care what spelling conventions you use for your resource identifiers. For example
https://www.merriam-webster.com/dictionary/get
https://www.merriam-webster.com/dictionary/post
https://www.merriam-webster.com/dictionary/put
https://www.merriam-webster.com/dictionary/update
These all work fine, just like every other resource on the web.
You absolutely can design your resource model so that editing the update_keys document also modifies the my-new-app document.
The potential difficulty is that general purpose components are not going to know what is going on. HTTP PUT means "update the representation of the target resource", and every general purpose component knows that; the origin server is allowed to modify other resources as a consequence of the changes to the "update-keys" resource.
But we don't have a great language for communicating the general purpose components all of the side effects that may have happened. Without some special magic, previously cached copies of my-new-app, with the original, unrotated, keys, will be left lying around. So the client may be left with a stale copy of the document that describes the app.
(An example of "some special magic" would be Linked Cache Invalidation, which affords describing caching relationships between resources using web linking. Unforunately, LCI has not been adopted as a standard, and you won't find the described link relations in the IANA registry.)

REST endpoint: how to proper design an action on a resource?

I have the resource /contracts with the following structure:
{
name: "Contract name",
signedAt: 123213231,
// etc.
}
While basic CRUD operations are well clear (GET /contracts, GET /contracts/{id}, POST /contracts, etc.) some doubts come when I have to do some concrete actions on the resource.
One of these actions is the following:
sign: means the contract is signed, so the signedAt date will need to be updated with the moment (date-time) the contract was signed.
So far I've been thinking about these different approaches:
PATCH-ing the resource
This approach will mean having the following endpoint method:
PATCH /contracts/{id}
and just posting the signedAt date { signedAt: 123213231 } meaning that after this the contract will be signed.
What I don't like about this approach is that the signature date comes from the client, I was thinking that having this date initialized on the backend side whenever a contract is signed could be better and more consistent.
Totally discarded, as the signedAt date should be set on the server
side exactly at the moment the sign is done.
POST-ing a new resource
This approach will mean having the signature action as a resource:
POST /contracts/{id}/sign
with an empty body in this case as we don't need to pass anything else so, once it is posted, the backend side would be the responsible for having the signature date initialized.
POST-ing the resource using 'action'
Similar to the previous approach, in this case I would use a query parameter called action on the contract resource:
POST /contracts/{idContract}?action=sign
also with an empty body where ?action=sign. Like in the previous approach, once posted the backend side would be the responsible for having the signature date initialized.
Questions
What would be the proper way to have this designed at a REST API level?
Is any of the approaches above close to a good design or not?
Would I need to modify any of the approaches?
Is there any better alternative?
I have designed a few rest APIs myself but I am not a restful evangelist so my answer might not be the best. I would suggest some of the following:
Create a custom converter for date values in your rest service that accepts date AND other specific fields. If you checkGoogle reporting APIs for example they allow you to use specific date range and also CURRENT_WEEK, CURRENT_MONTH etc. So you can add such specific value and use it. For example PATCH signedAt=CURRENT_DATE and the API handles that properly.
Add a boolean signed field to the resource. Do a POST or PATCH with signed=true. This way you will be able to eventually query only signed resources easily ;) Also it might be the case that people care only about if it is signed than when it was signed
I wouldn't use ?action=sign or /contracts/{id}/sign because these are not RESTFUL and even if you do use GET and POST you would use them in a way to create a workaround in order to implement actions in your design which shouldn't have actions
just posting the signedAt date { signedAt: 123213231 } meaning that after this the contract will be signed.
On HTTP Patch
The set of changes is represented in a format called a "patch document" identified by a media type.
Rather than rolling your own bespoke media type, you might want to consider whether one of the standard formats is suitable.
For example: JSON Patch.
Content-Type: application/json-patch+json
[ { "op": "replace", "path": "signedAt", "value": 123213231 }
JSON Merge Patch is another reasonable option
Content-Type: application/merge-patch+json
{ signedAt: 123213231 }
From what I can see, the primary difference is that JSON Patch provides a test operation which gives you finer grain control than simply relying upon validators
But you are absolutely right - PATCH gives the client code authority to specify the time value. If that's not appropriate for your use case, then PATCH is the wrong tool in the box.
POST /contracts/{id}/sign
POST /contracts/{idContract}?action=sign
As far as REST/HTTP are concerned, these two choices are equivalent -- you are updating the state of one resource by sending an unsafe request to a different resource. There are some mild differences in how these spellings act when resolving references, but as request-targets, it doesn't make a difference to the client.
An option that you seem to have overlooked:
POST /contracts/{id}
action=sign
This has the advantage that, when successful, you get cache invalidation for free.
In a hypermedia API, the flow might go something like this: the client would GET the resource; because the resource hasn't been signed yet, the representation could include a form, with a "sign" button on it. The action on the form would be /contracts/{id}. The consumer "signs" the contract by submitting the form -- the agent gathers up the information described by the form, encodes it into the request body, and then posts the request to the server. The server responds success, and the client's cache knows to invalidate the previously fetched copy of the resource.

Rest API designing PUT vs PATCH

I am developing 2 REST APIs which edits and pause something at my backend.
For editing I was using:
PUT /video/1
What is the best way to develop a pause video service. Should I use PATCH or PUT for this? Input would be just the id. If I use PUT then how can differentiate between edit and pause? And if I have another API to be developed for eg: video restart how can I accommodate these verbs in REST API?
Distinguishing the state using the HTTP method only is a poor idea. What you can is to:
Introduce state, and then use PATCH to change the state:
PATCH /vidoes/1
{
"state": "PLAYING|PAUSED|STOPPED" // what you need here
}
Mind don't patch like an idiot, however it is common to patch like an idiot.
Introduce new endpoints that will reflect the operation invoked on the resource - this is not fully RESTful, however also common:
POST /vidoes/1/play/
POST /vidoes/1/stop/
POST /vidoes/1/pause/
PUT for editing is ok of course, however remember that PUT is idempotent and requires the resource to be sent.
I do not agree with #Opal's answer here hence I post this answer. I do feel you use the wrong tools (or terms) to achieve what yo want. REST is more then just a HTTP invocation via a cleanly designed URI. As proposed by #Opal in a comment on his answer, WebSockets might be what you are looking for, though REST may be able to server your needs as well (as plain HTTP would do either).
Pausing a video
It should not be the task of the HTTP server to stop the video but the client. Usually partial GET requests are sent to the server retrieving only a portion of the resource and adding them to a buffer which the client reads. In the back the client site will issue further partial requests to keep the buffer filled while the client is reading it. If the client wants to pause, it simply stops reading the buffer and optionally stop sending further partial GET requests to the server.
This allows to spread the actual video onto mutliple servers and let the client talk to any of these and still get the correct responses. If the server has to maintain the client state, you need to ensure that the state is also replicated to all the other serving nodes. Sure, this is possible but also combined with higher overhead!
Updating videos
As you obviously create a video-editing system you have two options here as also suggested by the PUT definiton:
Partial content updates are possible by targeting a separately identified resource with state that overlaps a portion of the larger resource, or by using a different method that has been specifically defined for partial updates (for example, the PATCH method defined in RFC5789).
Separate the resource into smaller resources
Use an other method like PATCH
As already pointed out by #Opal in his answer, in case when you use PATCH to partially update a resource you should not only provide the new content within the body but also instruct the server what is should do with it.
The separation into smaller resources however does feel more natural to me for a video-editing system though. A video can be seen as a sequence of scenes which consist of numerous pictures and maybe an attached soundfile.
A movie therefore could be represented like this in pseudo Json-HAL:
Movie : {
title: The Matrix,
release_year: 1999,
actors: [Keanu Reeves, Laurence Fishburne, Carrie-Anne Moss, Hugo Weaving, Joe Pantoliano],
...
link: {
self: http://...,
...
},
embedded: {
Scenes : [
{
description: Trinity chased by police,
links: [
self: http://...,
video: http://.../scene01.vid
]
},
{
description: Thomas Anderson get notified to follow the white rabbit,
start_offset: 5091,
end_offset: 193920,
links: [
self: http://...,
video: http://.../scene02.vid
]
},
...
]
}
}
Instead of having all the bytes in one file you could maintain each scene separately. The movie representation combines the scenes to a full movie if played from scene 1 to scene n.
If now one scene is edited and the whole scene file should be replaced, using a simple PUT request is enough. If you want to trim the first or last few seconds off the video, you could introduce a start and stop offset for the respective scene and instead of reuploading the full scene again, you tell the client that it should start at the suggested offest or stop at the suggested position.
The client can use this parameters in the partial GET request to retrieve only the necessary bytes. This fields should then of course be modified via a PATCH command in order to prevent altering the video bytes or its URI. In order for a client to learn the total bytes of a video it can issue a HEAD request first to the URI and use the content length returned from the response
This, of course, screems for its own media-type, but this is what REST is actually all about. I don't know why so many misuse the REST-term for plain URI-design or think that a neat URI-API is more RESTful when REST doesn't care much about the URI layout actually.

RESTful, efficient way to query List.contains(element)?

Given:
/images: list of all images
/images/{imageId}: specific image
/feed/{feedId}: potentially huge list of some images (not all of them)
How would you query if a particular feed contains a particular image without downloading the full list? Put another way, how would you check whether a resource state contains a component without downloading the entire state? The first thought that comes to mind is:
Alias /images/{imageId} to /feed/{feedId}/images/{imageId}
Clients would then issue HTTP GET against /feed/{feedId}/images/{id} to check for its existence. The downside I see with this approach is that it forces me to hard-code logic into the client for breaking down an image URI to its proprietary id, something that REST frowns upon. Ideally I should be using the opaque image URI. Another option is:
Issue HTTP GET against /feed/{feedId}?contains={imageURI} to check for existence
but that feels a lot closer to RPC than I'd like. Any ideas?
What's wrong with this?
HEAD /images/id
It's unclear what "feed" means, but assuming it contains resources, it'd be the same:
HEAD /feed/id
It's tricky to say without seeing some examples to provide context.
But you could just have clients call HEAD /feed/images/{imageURI} (assuming that you might need to encode the imageURI). The server would respond with the usual HEAD response, or with a 404 error if the resource doesn't exist. You'd need to code some logic on the server to understand the imageURI.
Then the client either uses the image meta info in the head, or gracefully handles the 404 error and does something else (depending on the application I guess)
There's nothing "un-RESTful" about:
/feed/{feedId}?contains={imageURI}[,{imageURI}]
It returns the subset as specified. The resource, /feed/{feedid}, is a list resource containing a list of images. How is the resource returned with the contains query any different?
The URI is unique, and returns the appropriate state from the application. Can't say anything about the caching semantics of the request, but they're identical to whatever the caching semantics are of the original /feed/{feedid}, it simply a subset.
Finally, there's nothing that says that there even exists a /feed/{feedid}/image/{imageURL}. If you want to work with the sub-resources at that level, then fine, but you're not required to. The list coming back will likely just be a list of direct image URLS, so where's the link describing the /feed/{feedid}/image/{imageURL} relationship? You were going to embed that in the payload, correct?
How about setting up a ImageQuery resource:
# Create a new query from form data where you could constrain results for a given feed.
# May or may not redirect to /image_queries/query_id.
POST /image_queries/
# Optional - view query results containing URIs to query resources.
GET /image_queries/query_id
This video demonstrates the idea using Rails.