lreeder
lreeder

Reputation: 12226

Best practice for clearing record fields when using PATCH with JSON

I have a REST service that allows partial updates of a record using PATCH. For an example, let's say the REST service operates on a name record, where name has three fields - first, middle, and last name. A record for my user "Jane Q. User" looks like this in JSON:

{
  "first" : "Jane",
  "middle" : "Q.",
  "last" : "User" 
}

Now Jane Q. User has legally changed her last name to "Admin" and wants my service to quit displaying her middle name. Her new name is "Jane Admin". Should she submit a PATCH request with the new last name and the middle is set to the empty string to clear out her middle name like this?

PATCH /myservice/users/1

{
  "middle" : "",
  "last" : "Admin" 
}

Upvotes: 0

Views: 1652

Answers (2)

UniversE
UniversE

Reputation: 2507

I do not think there is a general best practice out there yet, but several ideas have been proposed. In this post I present three ideas (one of which is my own), but there may be many more.

JSON Patch

Website: http://jsonpatch.com/ RFC 6902: https://datatracker.ietf.org/doc/html/rfc6902

Example obtained from the website:

The original document

{
  "baz": "qux",
  "foo": "bar"
}

The patch

[
  { "op": "replace", "path": "/baz", "value": "boo" },
  { "op": "add", "path": "/hello", "value": ["world"] },
  { "op": "remove", "path": "/foo" }
]

The result

{
  "baz": "boo",
  "hello": ["world"]
}

This approach is very verbose and I suggest to use one of the libraries linked on website. It's not what you should implement over night yourself, imho, but it looks very powerful.

JSON Merge Patch

RFC 7396: https://datatracker.ietf.org/doc/html/rfc7396

Example from the spec:

{
    "a": "b",
    "c": {
        "d": "e",
        "f": "g"
    }
}

patched with

{
    "a":"z",
    "c": {
        "f": null
    }
}

will change the value of "a" to "z" and remove "f". However, many JSON deserialization libraries (e.g. for JVM based languages) do not distinguish between null and undefined, so this does not really solve the original problem, unless you are writing your own deserializer to parse that JSON in a way to preserve the information about the explicit null.

More discussion about these two can be found here:

https://erosb.github.io/post/json-patch-vs-merge-patch/

Clear Fields

This is what I came up with by myself to keep it very simple. It is a non-standard custom solution.

A document like

{
   "a": "foo",
   "b": "bar"
}

can be patched with

{
  "a": "baz",
  "_clear:" {
    "b:" true
  }
}

The main idea is to include boolean fields in a special "_clear" object for only the optional fields. That makes it explicit (in an auto generated documentation based on the DTO classes) which fields can be removed and which can not. Omitting a field or writing null or undefined are all treated as "do not modify this field".

The syntax (underscored field name) is inspired by HAL+JSON, but afaik that never exited draft status.

Upvotes: 2

Kevin
Kevin

Reputation: 1450

Mmmmm I'd not do it quite that way. If possible I'd recommend instead ask folks to issue a PUT

PUT /myservice/users/1
{
  "first" : "Jane",
  "last" : "User" 
}

And this would replace the user record.

However, depending on the potential size of the record, a PATCH is easier and so if you wanted to also/or use PATCH to update I'd recommend a null value.

PATCH /myservice/users/1
{
  "middle" : null
}

However... if your API returns empty values in responses (which personally I don't like), then for consistency an empty value would probably make a tad more sense..

PATCH /myservice/users/1
{
  "middle" : ""
}

However you end up doing it, just make sure you're consistent and when documenting your API, call out this functionality.

Cheers.

Upvotes: 0

Related Questions