I have an entity with several attributes, say «project». Apart from simple attributes, the project may have a list of «statuses» of which the last one is the current one. I have a web form to create/edit a project. All attributes of this project can be changed in this form, and also users can add a new status for the project (but they can’t change or delete old statuses).
Project statuses are purely composite entities, they don’t have any distinctive meaning or identity outside of the project scope, and they are not to be addressed directly, so they obviously don’t deserve a special root REST resource.
According to REST architecture, I created a resource called /projects. POST is used to create a new project, and PUT is used to change an existing project.
However, I don’t want the client to PUT the project together with all its historical statuses, firstly because this collection is too heavy, and secondly because the business logic allows only for adding statuses, not changing or deleting them, so PUTting the project together with all of its statuses doesn’t make any sense anyway.
PUTting a project with only a new status is also not an option, because it violates the idempotency of PUT.
I also don’t like the idea of POSTing a status in a second HTTP-request, say /project/{id}/status, because that would break the atomicity of the update operation from the user’s standpoint. If this second request gets lost on the wire, then the project will appear inconsistent to the user who edited it (the attributes changed, but the status stayed the same). Creating RESTful "transactions" seems like overkill (and also error prone) for this simple task of updating a seemingly monolithic entity.
This kind of problem is quite ubiquitous in my work, and may be generalized as such: what is the RESTfully correct and atomic way of updating a complex composite entity for which only partial update is allowed by the business logic?
I think that if you want to do partial updates (it's actually your case), you should use the method PATCH
. This allows to update either the project without dependencies (statuses) or the dependency(ies) without the project hints.
You can notice that there is a format to describe the operations to do within a method PATCH
. It's called JSON Patch (see https://www.rfc-editor.org/rfc/rfc6902). This format describes what you want to do within your request: add an element, update it, remove it, ...
I think that you could have something like that if you want (for example) to update the name of a specific project, remove a status (it's also a sample since I read that you want to forbid this!) and add a new one in one atomic request:
PATCH /projects/1
[
{
"op": "replace",
"path": "/name",
"value": "the new name of the project"
},
{
"op": "remove",
"path": "/statuses/1"
},
{
"op": "add",
"path": "/statuses/",
"value": {
"name": "my status",
(...)
}
}
]
Notice that you can put what you want in the attribute name
to identify the related element in the resource state. So /statuses/1
can be the second element in the array, the status with id of value 1
or something else.
The server-side processing for the request can be atomic.
I wrote a blog post about bulk updates: https://templth.wordpress.com/2015/05/14/implementing-bulk-updates-within-restful-services/. I think that the section "Implementing bulk updates" could correspond to what you look for.
Hope it helps you, Thierry
Do you need HTTP PATCH? It is the verb to express delta updates to a resource.
https://www.rfc-editor.org/rfc/rfc5789
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With