Should I use PATCH or PUT in my REST API?

214,799

Solution 1

The PATCH method is the correct choice here as you're updating an existing resource - the group ID. PUT should only be used if you're replacing a resource in its entirety.

Further information on partial resource modification is available in RFC 5789. Specifically, the PUT method is described as follows:

Several applications extending the Hypertext Transfer Protocol (HTTP) require a feature to do partial resource modification. The existing HTTP PUT method only allows a complete replacement of a document. This proposal adds a new HTTP method, PATCH, to modify an existing HTTP resource.

Solution 2

The R in REST stands for resource

(Which isn't true, because it stands for Representational, but it's a good trick to remember the importance of Resources in REST).

About PUT /groups/api/v1/groups/{group id}/status/activate: you are not updating an "activate". An "activate" is not a thing, it's a verb. Verbs are never good resources. A rule of thumb: if the action, a verb, is in the URL, it probably is not RESTful.

What are you doing instead? Either you are "adding", "removing" or "updating" an activation on a Group, or if you prefer: manipulating a "status"-resource on a Group. Personally, I'd use "activations" because they are less ambiguous than the concept "status": creating a status is ambiguous, creating an activation is not.

  • POST /groups/{group id}/activation Creates (or requests the creation of) an activation.
  • PATCH /groups/{group id}/activation Updates some details of an existing activation. Since a group has only one activation, we know what activation-resource we are referring to.
  • PUT /groups/{group id}/activation Inserts-or-replaces the old activation. Since a group has only one activation, we know what activation-resource we are referring to.
  • DELETE /groups/{group id}/activation Will cancel, or remove the activation.

This pattern is useful when the "activation" of a Group has side-effects, such as payments being made, mails being sent and so on. Only POST and PATCH may have such side-effects. When e.g. a deletion of an activation needs to, say, notify users over mail, DELETE is not the right choice; in that case you probably want to create a deactivation resource: POST /groups/{group_id}/deactivation.

It is a good idea to follow these guidelines, because this standard contract makes it very clear for your clients, and all the proxies and layers between the client and you, know when it is safe to retry, and when not. Let's say the client is somewhere with flaky wifi, and its user clicks on "deactivate", which triggers a DELETE: If that fails, the client can simply retry, until it gets a 404, 200 or anything else it can handle. But if it triggers a POST to deactivation it knows not to retry: the POST implies this.
Any client now has a contract, which, when followed, will protect against sending out 42 emails "your group has been deactivated", simply because its HTTP-library kept retrying the call to the backend.

Updating a single attribute: use PATCH

PATCH /groups/{group id}

In case you wish to update an attribute. E.g. the "status" could be an attribute on Groups that can be set. An attribute such as "status" is often a good candidate to limit to a whitelist of values. Examples use some undefined JSON-scheme:

PATCH /groups/{group id} { "attributes": { "status": "active" } }
response: 200 OK

PATCH /groups/{group id} { "attributes": { "status": "deleted" } }
response: 406 Not Acceptable

Replacing the resource, without side-effects use PUT.

PUT /groups/{group id}

In case you wish to replace an entire Group. This does not necessarily mean that the server actually creates a new group and throws the old one out, e.g. the ids might remain the same. But for the clients, this is what PUT can mean: the client should assume he gets an entirely new item, based on the server's response.

The client should, in case of a PUT request, always send the entire resource, having all the data that is needed to create a new item: usually the same data as a POST-create would require.

PUT /groups/{group id} { "attributes": { "status": "active" } }
response: 406 Not Acceptable

PUT /groups/{group id} { "attributes": { "name": .... etc. "status": "active" } }
response: 201 Created or 200 OK, depending on whether we made a new one.

A very important requirement is that PUT is idempotent: if you require side-effects when updating a Group (or changing an activation), you should use PATCH. So, when the update results in e.g. sending out a mail, don't use PUT.

Solution 3

I would recommend using PATCH, because your resource 'group' has many properties but in this case, you are updating only the activation field(partial modification)

according to the RFC5789 (https://www.rfc-editor.org/rfc/rfc5789)

The existing HTTP PUT method only allows a complete replacement of a document. This proposal adds a new HTTP method, PATCH, to modify an existing HTTP resource.

Also, in more details,

The difference between the PUT and PATCH requests is reflected in the way the server processes the enclosed entity to modify the resource
identified by the Request-URI. In a PUT request, the enclosed entity is considered to be a modified version of the resource stored on the
origin server, and the client is requesting that the stored version
be replaced. With PATCH, however, the enclosed entity contains a set of instructions describing how a resource currently residing on the
origin server should be modified to produce a new version. The PATCH method affects the resource identified by the Request-URI, and it
also MAY have side effects on other resources; i.e., new resources
may be created, or existing ones modified, by the application of a
PATCH.

PATCH is neither safe nor idempotent as defined by [RFC2616], Section 9.1.

Clients need to choose when to use PATCH rather than PUT. For
example, if the patch document size is larger than the size of the
new resource data that would be used in a PUT, then it might make
sense to use PUT instead of PATCH. A comparison to POST is even more difficult, because POST is used in widely varying ways and can
encompass PUT and PATCH-like operations if the server chooses. If
the operation does not modify the resource identified by the Request- URI in a predictable way, POST should be considered instead of PATCH
or PUT.

The response code for PATCH is

The 204 response code is used because the response does not carry a message body (which a response with the 200 code would have). Note that other success codes could be used as well.

also refer thttp://restcookbook.com/HTTP%20Methods/patch/

Caveat: An API implementing PATCH must patch atomically. It MUST not be possible that resources are half-patched when requested by a GET.

Solution 4

Since you want to design an API using the REST architectural style you need to think about your use cases to decide which concepts are important enough to expose as resources. Should you decide to expose the status of a group as a sub-resource you could give it the following URI and implement support for both GET and PUT methods:

/groups/api/groups/{group id}/status

The downside of this approach over PATCH for modification is that you will not be able to make changes to more than one property of a group atomically and transactionally. If transactional changes are important then use PATCH.

If you do decide to expose the status as a sub-resource of a group it should be a link in the representation of the group. For example if the agent gets group 123 and accepts XML the response body could contain:

<group id="123">
  <status>Active</status>
  <link rel="/linkrels/groups/status" uri="/groups/api/groups/123/status"/>
  ...
</group>

A hyperlink is needed to fulfill the hypermedia as the engine of application state condition of the REST architectural style.

Solution 5

One possible option to implement such behavior is

PUT /groups/api/v1/groups/{group id}/status
{
    "Status":"Activated"
}

And obviously, if someone need to deactivate it, PUT will have Deactivated status in JSON.

In case of necessity of mass activation/deactivation, PATCH can step into the game (not for exact group, but for groups resource:

PATCH /groups/api/v1/groups
{
    { “op”: “replace”, “path”: “/group1/status”, “value”: “Activated” },
    { “op”: “replace”, “path”: “/group7/status”, “value”: “Activated” },
    { “op”: “replace”, “path”: “/group9/status”, “value”: “Deactivated” }
}

In general this is idea as @Andrew Dobrowolski suggesting, but with slight changes in exact realization.

Share:
214,799
java_geek
Author by

java_geek

Updated on January 08, 2020

Comments

  • java_geek
    java_geek over 4 years

    I want to design my rest endpoint with the appropriate method for the following scenario.

    There is a group. Each group has a status. The group can be activated or inactivated by the admin.

    Should I design my end point as

    PUT /groups/api/v1/groups/{group id}/status/activate
    

    OR

    PATCH /groups/api/v1/groups/{group id}
    
    with request body like 
    {action:activate|deactivate}
    
  • thecoshman
    thecoshman almost 10 years
    To be fair, you could PUT the string 'activate' or 'deactivate' to the resource. As there (seems) to only be the one thing to toggle, completely replacing it is not such a huge deal. And it does allow for a (insignificantly) smaller request.
  • fishpen0
    fishpen0 over 8 years
    It is important to note that RFC 5789 is still in proposal phase and has not been officially accepted and is currently flagged as 'irrata exist'. This 'best practice' is highly debated and technically PATCH is not yet part of the HTTP standard.
  • Honinbo Shusaku
    Honinbo Shusaku over 7 years
    This was very informative to me. "This pattern is useful when the "activation" of a group has side-effects" - How come this pattern is useful, specifically in regards to when actions have side effects, as opposed to the OP initial endpoints
  • berkes
    berkes over 7 years
    @Abdul, the pattern is useful for a lot of reasons, but wrt side-effects, it should be very clear to a client, what effects an action has. When, say, an iOS app decides to send the entire addressbook as "contacts" it should be extremely clear what side-effects the create, update, delete etc. of a Contact has. To avoid mass-mailing all contacts, for example.
  • kojow7
    kojow7 about 7 years
    Thank you for setting me straight. I mistakenly understood that if I wanted to do an update on everything except for the ID I should be using PATCH instead of PUT.
  • Piotr Kula
    Piotr Kula almost 7 years
    In RESTfull PUT can also change the entities Identity - For example the PrimaryKey ID where it could cause a parallel request to fail. (for example updating the entire entity needs to delete some rows and add new ones, hence creating new entities) Where PATCH must never be able to do that, allowing for unlimited number of PATCH requests without affecting other "applications"
  • Jono Stewart
    Jono Stewart over 6 years
    Just my 2 cents a few years later: you could consider the status itself to be a resource, and if so, using PUT against /status would technically be replacing the status resource at that end point.
  • Mladen B.
    Mladen B. about 6 years
    I would dare to argue against the docs, even though it's "the" RFC. The docs state that you should use PATCH to modify just a part of a resource, but it omitted the important thing that PATCH method is defined as a non-idempotent method. Why? If the PUT method was created with the update/replacement of the whole resource in mind, then why wasn't PATCH method created as an idempotent method like PUT, if its purpose was to just update the part of a resource? To me, it looks more of a difference in idempotency of update, like "a=5" (PUT) and "a=a+5" (PATCH). Both can update the entire resource.
  • Mladen B.
    Mladen B. about 6 years
    Very helpful answer. Thanks! I would also add a comment, just like in Luke's answer, pointing out that the difference between PUT/PATCH is not just the whole/partial update, it's also the idempotency that's different. This wasn't a mistake, it was an intentional decision and I think not a lot of people take this into the consideration, when deciding the HTTP method's usage.
  • Pepito Fernandez
    Pepito Fernandez almost 6 years
    I agree and disagree. RESTful API shouldn't reflect your domain. They tend to model the applications' use cases more than the business. There is a reason why RESTful apis follow RFC 2616. As a consumer I have no clue what the "side effects" of your business operations are. All I know is that your HTTP VERBS should reflect the operation on the RESOURCE. Therefore if a DELETE is idempotent, it means than the operation on the RESOURCE is so. Not the "side effects". Sending emails are not violations of "idempotency". That's a business concern, not a RESTful api.
  • rich remer
    rich remer over 5 years
    An "activate" might not be a thing, but an "activate service" is most definitely a thing. If your rel is service, then your comment about "verbs" in URLs is irrelevant.
  • berkes
    berkes over 5 years
    @richremer I disagree. Services are not Nouns in practice. Technically an ActivateService is a noun, true. But in practice it is just a verb hiding behind a noun. Same for Agent (ActivateAgent), Maker (ActiveMaker), Factory (ActivateFactory) or other service-alike models. A REST interface that allows interaction with such verb-ishms is really an RPC interface decorated as REST. It has all the downsides of REST, and hardly the benefits. Furthermore: IMO Services are internal implementation details and should not be exposed.
  • rich remer
    rich remer over 5 years
    I'm not sure I understand what you mean by that last bit. What's the point of the long-standing "service" relationship if services should not be exposed?
  • berkes
    berkes over 5 years
    @richremer services, like models, are internal abstractions. Just as it is a poor abstraction to require a 1-1 relation between REST-endpoints-and-ORM-models or even database tables, it is poor abstraction to expose Services. The outside would, your API, must communicate domain models. How you implement them internally is of no concern to the API. You should be free to move from a ActivationService to a CQRS based activation flow, without having to change your API.
  • rich remer
    rich remer over 5 years
    It's a poor abstraction to attempt to map domain models and REST interfaces 1-to-1, but we can agree to disagree. Though, I still would like to understand what you think of the role of rel="service".
  • berkes
    berkes over 5 years
    @richremer I'm not sure if I understand you. But rel=service is used in rfc5023 (Atom) to link to the Services document. In a RESTfull API, there is no place for Services, since it is about Resources. Sure, you can link to an RPC endpoint from a REST-ful response, but that smells really bad and gives hardly any benefits, IMO.
  • Maggyero
    Maggyero about 5 years
    "A very important requirement is that PUT is idempotent: if you require side-effects when updating a Group (or changing an activation), you should use PATCH." Sorry but PUT has side effects, since it updates a resource. Having no side effects is defined as being "safe" in RFC 7231, and PUT method is not safe, only GET, HEAD, OPTIONS, and TRACE methods are in the RFC.
  • berkes
    berkes about 5 years
    @Maggyero 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. Of the request methods defined by this specification, PUT, DELETE, and safe request methods are idempotent.. So, no it is not safe. But it should be idempotent!
  • AnorZaken
    AnorZaken about 5 years
    You are using 406 in a very stange way. 406 means that the server was unable to send a response back to the client because the response cannot be formatted into any of the formats specified by the client in its accept header. 406 does not imply that the requested operation failed, only that the server was unable to send back a response. E.g. you could request a file upload to the server, the file upload succeeds, and you could still get a 406 response. See: what-is-406-not-acceptable-response.