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

3

6

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?

Godfrey answered 6/6, 2020 at 8:29 Comment(0)
D
6

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
Dominy answered 6/6, 2020 at 9:50 Comment(1)
In a REST architecture there should be no need to document how a server is processing a request. This is some internal detail. All the client is interested in is to get its take on a resource back to the server. What concrete HTTP operation has to be used should be taught by the server through i.e. the negotiated media-type (i.e. HTML's form element). This is in essence what HATEOAS is all about, using given hypertext controls to drive the state of a resource. Partial update may also be achievable through overlapping resources as indicated by RFC 7231Kierkegaard
G
0

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"
}
Gipson answered 6/6, 2020 at 9:35 Comment(0)
F
0

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.

Frankel answered 6/6, 2020 at 13:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.