REST - Does the PUT method have to remove an optional field when it is omitted?

Viewed 3612

I have a resource Car which have some required fields and another optional ones.

The Car was created with the following request:

POST /cars
{
  plate: "XYZ-A2C4",
  color: "blue,
  owner: "John" //OPTIONAL
}

A REST client wants to update all required info of this car:

PUT /cars/:id
{
  plate: "ABC-1234",
  color: "black"
}

What happen to the owner optional field?

  • It will be removed, since it was not informed? ie: PUT must replace the entire resource with the representation passed in the payload ?
  • Or, since owner is not required, the server may preserve the old value?


I know that the server can provide a PATCH method, but sometimes it is not possible to update a single field because the new state could become invalid (there are no minimum required payload to enforce related fields values). Also, manipulating arrays, removing fields or setting it with null can be tricky in some cases with PATCH since it can be done with two different patterns; JSON Merge Patch is limited and JSON Patch is kinda strange.


Is it OK to perform a PUT with the required fields and the server preserves the old optional values when it is omitted?

3 Answers

If you want to go by the book (being section 4.3.4 of RFC 7231), then yes, the PUT request should, in your case, replace the entire Car resource:

The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload. 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.

So, by the book, you should not use PUT for partial updates, but rather PATCH.

However, in practice, it is really up to you to decide how exactly this is applicable to your service, and more importantly, to document it.

Here are a few real-world examples of how some well-known APIs allow partial updates:

  • The Ghost API does not support partial resource update, it requires a PUT request with a full resource for any update
  • The Rossum API supports PATCH for partial resource update, but their documentation explicitly states that only top-level properties are supported
  • GitHub allows both PATCH and POST requests for partial data updates
  • Firebase allows PATCH requests but also POST with an X-HTTP-Method-Override header

You are exactly right that sometimes, a PATCH method could result in an invalid resource if processes as-is. However nothing prevents the server from ensuring proper data state as a side-effect. Therefore, during each call you can have the server:

  • verify the proper state of the resource before persisting it
  • reject (with a 400 Bad Request error) any request that would result in improper state
  • respond with the resource (maybe bearing side-effects) on success

Strictly speaking PUT should replace the resource being identified with the entity that is being supplied. In your example, that would mean car would be replaced without the optional field unless the optional field was also supplied in the PUT request.

The number of APIs that strictly adhere to REST or a resource oriented architecture are pretty few and far between, so I personally would try not to sweat this level of detail and just document your api and the behavior that your users can expect.

If you really want to be fanatical about it, I think you're on the right track with PATCH, or you could identify a sub-resource:

PUT /cars/:id/plate
"ABC-1234"

PUT /cars/:id/color
"black

OR perhaps:

PUT /cars/:id/description
{
  plate: "ABC-1234",
  color: "black"
}

The www-tag mailing list archives include this interesting observation from Roy Fielding in 2002:

HTTP does not attempt to require the results of a GET to be safe. What it does is require that the semantics of the operation be safe, and therefore it is a fault of the implementation, not the interface or the user of that interface, if anything happens as a result that causes loss of property (money, BTW, is considered property for the sake of this definition).

The specification for HTTP PUT should be understood the same way; the specification tells us what the messages mean, but not how to do it.

The PUT method requests that the state of the target resource be created or replaced with the state defined by the representation enclosed in the request message payload.

PUT semantics are fundamentally "remote authoring"; the request tells the server to make its copy of a resource look like a clients copy.

So when you leave an "optional" element out of the representation provided in the request, you are telling the server to remove that optional element from its own representation as well.

It's the responsibility of the client to create a message that describes what the actually wants. So if you the client intend that the optional fields are unchanged after your request, you need to include them in the representation that you include in the body of the request.

The server is expected to interpret the PUT request as described; but it is not constrained in what it does with such a thing (subject to Fielding's observation above: which way does the blame finger point when things go wrong?)

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.

What happen to the owner optional field?

So in your specific example, the request clearly says "do not include the owner element". But the server is allowed to ignore that; it need only be careful in crafting its response not to imply that the provided representation was stored unchanged.

Related