Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice for partial updates in a RESTful service

Tags:

rest

People also ask

Which REST method should be used for partial updates to an existing record?

PUT. The HTTP PATCH method should be used whenever you would like to change or update just a small part of the state of the resource. You should use the PUT method only when you would like to replace the resource in its entirety.

Can you do a partial update?

Partial updates are allowed by PUT (according to RFC 7231 https://www.rfc-editor.org/rfc/rfc7231#section-4.3.4). ",... PUT request is defined as replacing the state of the target resource." - replacing part of object basically change state of it.

Which HTTP method will perform partial update to a resource?

The HTTP PATCH request method applies partial modifications to a resource. PATCH is somewhat analogous to the "update" concept found in CRUD (in general, HTTP is different than CRUD, and the two should not be confused). A PATCH request is considered a set of instructions on how to modify a resource.


You basically have two options:

  1. Use PATCH (but note that you have to define your own media type that specifies what will happen exactly)

  2. Use POST to a sub resource and return 303 See Other with the Location header pointing to the main resource. The intention of the 303 is to tell the client: "I have performed your POST and the effect was that some other resource was updated. See Location header for which resource that was." POST/303 is intended for iterative additions to a resources to build up the state of some main resource and it is a perfect fit for partial updates.


You should use POST for partial updates.

To update fields for customer 123, make a POST to /customer/123.

If you want to update just the status, you could also PUT to /customer/123/status.

Generally, GET requests should not have any side effects, and PUT is for writing/replacing the entire resource.

This follows directly from HTTP, as seen here: http://en.wikipedia.org/wiki/HTTP_PUT#Request_methods


You should use PATCH for partial updates - either using json-patch documents (see https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-json-patch-08 or http://www.mnot.net/blog/2012/09/05/patch) or the XML patch framework (see https://www.rfc-editor.org/rfc/rfc5261). In my opinion though, json-patch is the best fit for your kind of business data.

PATCH with JSON/XML patch documents has very strait forward semantics for partial updates. If you start using POST, with modified copies of the original document, for partial updates you soon run into problems where you want missing values (or, rather, null values) to represent either "ignore this property" or "set this property to the empty value" - and that leads down a rabbit hole of hacked solutions that in the end will result in your own kind of patch format.

You can find a more in-depth answer here: http://soabits.blogspot.dk/2013/01/http-put-patch-or-post-partial-updates.html.


I am running into a similar problem. PUT on a sub-resource seems to work when you want to update only a single field. However, sometimes you want to update a bunch of things: Think of a web form representing the resource with option to change some entries. The user's submission of form should not result in a multiple PUTs.

Here are two solution that I can think of:

  1. do a PUT with the entire resource. On the server-side, define the semantics that a PUT with the entire resource ignores all the values that haven't changed.

  2. do a PUT with a partial resource. On the server-side, define the semantics of this to be a merge.

2 is just a bandwidth-optimization of 1. Sometimes 1 is the only option if the resource defines some fields are required fields (think proto buffers).

The problem with both these approaches is how to clear a field. You will have to define a special null value (especially for proto buffers since null values are not defined for proto buffers) that will cause clearing of the field.

Comments?


For modifying the status I think a RESTful approach is to use a logical sub-resource which describes the status of the resources. This IMO is pretty useful and clean when you have a reduced set of statuses. It makes your API more expressive without forcing the existing operations for your customer resource.

Example:

POST /customer/active  <-- Providing entity in the body a new customer
{
  ...  // attributes here except status
}

The POST service should return the newly created customer with the id:

{
    id:123,
    ...  // the other fields here
}

The GET for the created resource would use the resource location:

GET /customer/123/active

A GET /customer/123/inactive should return 404

For the PUT operation, without providing a Json entity it will just update the status

PUT /customer/123/inactive  <-- Deactivating an existing customer

Providing an entity will allow you to update the contents of the customer and update the status at the same time.

PUT /customer/123/inactive
{
    ...  // entity fields here except id and status
}

You are creating a conceptual sub-resource for your customer resource. It is also consistent with Roy Fielding's definition of a resource: "...A resource is a conceptual mapping to a set of entities, not the entity that corresponds to the mapping at any particular point in time..." In this case the conceptual mapping is active-customer to customer with status=ACTIVE.

Read operation:

GET /customer/123/active 
GET /customer/123/inactive

If you make those calls one right after the other one of them must return status 404, the successful output may not include the status as it is implicit. Of course you can still use GET /customer/123?status=ACTIVE|INACTIVE to query the customer resource directly.

The DELETE operation is interesting as the semantics can be confusing. But you have the option of not publishing that operation for this conceptual resource, or use it in accordance with your business logic.

DELETE /customer/123/active

That one can take your customer to a DELETED/DISABLED status or to the opposite status (ACTIVE/INACTIVE).