HTTP PATCH a joined table?

284 Views Asked by At

The RFC 6902 defines a lot of things and clearly. Except two related things:

  1. How do you patch a table that is joined to the resource?
  2. And when the join has multiple rows?

Eg.

GET /news/123

[
    {"title": "Hello", "contents": "World", "categories": [1, 2, 3]}
]

Where the results come from these DB tables and columns:

title = news.title
contents = news.contents
categories = newscategories_mapping WHERE newscategories_mapping.newsID = news.id

So, if I send a patch:

PATCH /news/123
[
    {"op": "replace", "path": "/title", "value": "New Title"}
]

It is very straightforward. But how about when I want to update the Categories of the same resource? Ie. the "joined" table.

PATCH /news/123
[
    {"op": "replace", "path": "/categories", "value": [4, 5]}
]

Also, notice two things:

  • None of the existing values are included.
  • The amount is less than the original (2 values instead of 3)

Let's say the original values before the patch are in newscategories_mapping.id 20, 21 and 22.

Question 1: Should the new /categories value 4 replace id=20, and value 5 replaces id=21? Or should id=20, id=21 and id=22 be deleted and the new values get new IDs id=23 and id=24?

Question 2: Should id=22 be deleted, even though that was not explicitly requested?

Question 3: Or should the above not be done at all, and I should instead have a separate endpoint for patching each of the Categories? So, eg. PATCH /news/123/categories/20 and PATCH /news/123/categories/21?

1

There are 1 best solutions below

1
On

You can add multiple JSON Patch segments to your request. Either all steps get applied or none at all though so apply them within a transaction!

PATCH /news/123
[
    {"op": "replace", "path": "/title", "value": "New Title"},
    {"op": "replace", "path": "/categories", "value": [4, 5]}
]

Question 1: Should the new /categories value 4 replace id=20, and value 5 replaces id=21? Or should id=20, id=21 and id=22 be deleted and the new values get new IDs id=23 and id=24?

The RFC states explicitely The "replace" operation replaces the value at the target location with a new value. Hence the values after applying the patch should be 4 and 5. You basically replace the existing list with the list given in the patch operation.

Question 2: Should id=22 be deleted, even though that was not explicitly requested?

yes

Question 3: Or should the above not be done at all, and I should instead have a separate endpoint for patching each of the Categories? So, eg. PATCH /news/123/categories/20 and PATCH /news/123/categories/21?

A patch is a simple instructions calculated by the client the server should perform to transform the target resource to the intended outcome. If you want to include 22 in the final state of the categories element you have two options:

  • Add the old value in the value you send in the replace operation to the list you want to set
  • Add an additional add operation where you explicitely add the desired value after the others got removed

A client should always retrieve the latest state from the server before sending in a patch request. Often the modifications should be applied on the most recent state and hence ETag headers are used to utilize optimistic locking and prevent updates on outdated states.