How to model a CANCEL action in a RESTful way?

11,200

Solution 1

Exposing the GET interface of a resource is not compulsory.

For example, use

PUT /api/tickets/{id}/actions/cancel

to submit the cancellation request. I choose PUT since there would be no more than one cancellation request in effect.

Hope it be helpful.

Solution 2

Take a look what exactly is RESTful way. No matter if you send PATCH request with isCancelled as payload or even DELETE if you want tickets to disappear. It's still RESTful.

Your move depends on your needs. As you said

I have considered creating a new resource CancelledTickets, but in our domain we would never have the need to a GET on cancelled tickets.

I would just send DELETE. You don't have to remove it physically. If it's possible to un-cancel, then implement isCancelled mechanism. It's just question of taste.

Solution 3

REST is basically a generalization of the browser based Web. Any concepts you apply for the Web can also be applied to REST.

So, how would you design a cancel activity in a Web page? You'd probably have a table row with certain activities like edit and delete outlined with icons and mouse-over text that on clicking invoke a URI on the server and lead to a followup state. You are not that much interested how the URI of that button might look like or if a PATCH or DELETE command is invoked in the back. You are just interested that the request is processed.

The same holds true if you want to perform the same via REST. Instead of images that hint the user that an edit or cancel activity is performed on an entry, a meaningful link-relation name should be used to hint the client about the possiblities. In your case this might be something like reserve new tickets, edit reservation or cancel reservation. Each link relation name is accompanied by a URL the client can invoke if he wants to perform one of the activities. The exact characters of the URI is not of importance here to the client also. Which method to invoke might either be provided already in the response (as further accompanying field) or via the media type the response was processed for. If neither the media type nor an accompanying field gives a hint on which HTTP operation to use an OPTIONS request may be issued on the URI beforehand. The rule of thumb here is, the server should teach a client on how to achieve something in the end.

By following such a concept you decouple a client from the API and make it robust to changes. Instead of a client generating a URI to invoke the client is fed by the server with possible URIs to invoke. If the server ever changes its iternal URI structure a client using one of the provided URIs will still be able to invoke the service as it simply used one of the URIs provided by the server. Which one to use is determined by analyzing the link relation name that hints the client when to invoke such URI. As mentioned above, such link relation names need to be defined somewhere. But this is exactly what Fielding claimed back in 2008 by:

A REST API should spend almost all of its descriptive effort in defining the media type(s) used for representing resources and driving application state, or in defining extended relation names and/or hypertext-enabled mark-up for existing standard media types. (Source)

Which HTTP operation to choose for canceling a ticket/reservation may depend on your desing. While some of the answers recommended DELETE RFC 7231 states that only the association between the URI and the resource is removed and no gurantee is given that the actual resource is also being removed here as well. If you design a system where a cancelation might be undone, then DELETE is not the right choice for you as the mapping of the URI to the resource should not exist further after handling a DELETE request. If you, however, consider a cancelation to also lead to a removal of the reservation then you MAY use DELETE.

If you model your resource in a way that maintains the state as property within the resource, PATCHing the resource might be a valid option. However, simply sending something like state=canceled is probably not enough here as PATCH is a calculation of steps done by the client in order to transform a certain resource (or multipe resources) into a desired target state. JSON Patch might give a clue on how this might be done. A further note needs to be done on the atomicy requirement PATCH has. Either all of the instructions succeed or none at all.

As also PUT was mentioned in one of the other answers. PUT has the semantics of replacing the current representation available at the given URI with the one given in the request' payload. The server is further allowed to either reject the content or transform it to a more suitable representation and also affect other resources as well, i.e. if they mimic a version history of the resource.

If neither of the above mentioned operations really satisfies your needs you should use POST as this is the all-purpose, swiss-army-knife toolkit of HTTP. While this operation is usually used to create new resources, it isn't limited to it. It should be used in any situation where the semantics of the other operations aren't applicable. According to the HTTP specification

The POST method requests that the target resource process the representation enclosed in the request according to the resource's own specific semantics.

This is basically the get-free-out-of-jail card. Here you can literally process anything at the server according to your own rules. If you want to cancel or pause something, just do it.

I highly discourage to use GET for creating, altering or canceling/removing something. GET is a safe operation and gurantees any invoking client that it wont alter any state for the invoked resource on the server. Note that it might have minor side-effects, i.e. logging, though the actual state should be unaffected by an invocation. This is a property Web crawler rely on. They will simply invoke any URI via GET and learn the content of the received response. And I assume you don't want Google (or any other crawler) to cancel all of your reservations, or do you?

As mentioned above, which HTTP operation you should use depends on your design. DELETE should only be used if you are also going to remove the representation, eventhough the spec does not necessarily require this, but once the URI mapping to the resource is gone, you basically have no way to invoke this resource further (unless you have created a further URI mapping first, of course). If you designed your resource to keep the state within a property I'd probably go for PATCH but in general I'd basically opt for POST here as here you have all the choices at your hands.

Share:
11,200
Karthik Balasubramanian
Author by

Karthik Balasubramanian

Updated on June 13, 2022

Comments

  • Karthik Balasubramanian
    Karthik Balasubramanian almost 2 years

    We are currently in the process of wrangling smaller services from our monoliths. Our domain is very similar to a ticketing system. We have decided to start with the cancellation process of the domain.

    Our cancel service has as simple endpoint "Cancel" which takes in the id of the ticket. Internally, we retrieve the id, perform some operations related to cancel on it and update the state of the entity in the store. From the store's perspective the only difference between a cancelled ticket and a live ticket are a few properties.

    From what I have read, PATCH seems to be the correct verb to be used in this case, as am updating only a simple property in the resource.

    PATCH /api/tickets/{id}
    Payload {isCancelled: true}
    

    But isCancelled is not an actual property in the entity. Is it fair to send properties in the payload that are not part of the entity or should I think of some other form of modeling this request? I would not want to send the entire entity as part of the payload, since it is large.

    I have considered creating a new resource CancelledTickets, but in our domain we would never have the need to a GET on cancelled tickets. Hence stayed away from having to create a new resource.

  • Karthik Balasubramanian
    Karthik Balasubramanian about 7 years
    As i understand being RESTful means that we have to comply Roy Fieldig's constraints. Complying to these standards means that any client who uses the service can make certain assumptions about the service. That being said, I also understand being functional is more important that being compliant. Thoughts?
  • Shashank Bodkhe
    Shashank Bodkhe over 5 years
    This example demonstrates usage of Cancel Request by deletion of the resource(Row of a DB, Some file, etc).We can use PUT which "Updates" the resource by changing some value(Some Boolean value, toggle On -Off, etc ) in DB.
  • ophychius
    ophychius about 5 years
    I feel creating a cancelation / cancel resource is the way to go. Clearly they are not setting a cancelled/isCancelled field on the ticket resource, as OP says there is more to it. So it only seems fair to be able to send a cancel resource.
  • Collin Thomas
    Collin Thomas about 3 years
    This is not RESTful because cancel is a verb and verbs are reserved for the HTTP method
  • Roman Vottner
    Roman Vottner over 2 years
    @CollinThomas The characters in a URI have no influence whether a service is RESTful or not! Hence, a URI that ends with a verb is just as "RESTful" as an URI ending with a noun or nonsense at all. Actually, there is no such thing as a "RESTful URL/URI". This myth unfortunately adds so much to the confusion in the domain of REST.
  • Roman Vottner
    Roman Vottner over 2 years
    Not only do the respective "states" need documentation. Your first request would i.e. replace the current representation with the one of the payload. Hence request 3 can't return you anticipated payload if you adhere to the rules of RFC 7231. Next, you assume some relation between URIs of request 1/3 and 2 though if there is an intermediary cache it wont know that these are somehow related and won't know that it should invalidate any stored representation for the URI in request 2 also. Hence a cache might serve a client with outdated content. Note caching is one of the few constraints of REST