The following is the structure of my content in MongoDB
{
partnerName: "Zumba Fitness",
supportedProducts: [
"Fitness 1.0",
"Protein Bars"
]
}
I want to modify/update the contents of supportedProducts upon a PATCH request. For example, I want to change Fitness 1.0 to Fitness 2.0
I am unsure how the request for PATCH from the client side will look like. Would be it something as following
PATCH /data/{partnerName}
Content-Type: application/json
[
{ op: "replace", path: "/supportedProducts", value: "Fitness 2.0" }
]
I have tried the above, but it did not modify the content in my database
RFC 6902 says the media type for JSON Patch is
application/json-patch+json
I have tried the above, but it did not modify the content in my database
I wouldn't expect that to happen by magic; you are still going to need to invoke code that will apply the patch. For example: jsonpatch-to-mongodb
Related
I have the following resources in my system 1. Services 2. Features where a feature has the following JSON structure,
{
id: "featureName",
state: "active",
allowList: [serviceID1, serviceID2],
denyList: [serviceID3, serviceID4]
}
I am trying to update the allowList or denyList which consists of serviceIDs and thinking of using PATCH method to do it like below,
/features/{featureId}/allowlist
/features/{featureId}/denylist
/features/{featureName}/state/{state}
My first question is should I even include allowlist, state, denylist in the url as my resources are services and features, not the allowlist or denylist.
How should the rest endpoint look like?
After reading thread mentioned below I was thinking about restructuring urls as below,
/features/{featureId}
[
{ "op": "add", "path": "/allowList", "value": [ "serviceA", "serviceB"]},
{ "op": "update", "path": "/state", "value": false}
]
Lastly, the use of PATCH even justified here? or there is any better way to design the api.
Note: I have got some help from the thread REST design for update/add/delete item from a list of subresources but have not used patch often.
How should the rest endpoint look like?
The URI that you use to edit (PUT, PATCH) a resource should look the same as the URI that you use to read (GET) the resource. The motivation for this design is cache-invalidation; your successful writes automatically invalidate previously cached reads of the same resource (same URI).
Lastly, the use of PATCH even justified here? or there is any better way to design the api.
In this example, the representation of the document is small compared to the HTTP headers, and the size of your patch document is close to the size of the resource representation. If that's the typical case, I'd be inclined to use PUT rather than PATCH. PUT has idempotent semantics, which general purpose components can take advantage of (for example, automatically resending requests when the response to an earlier request has been lost on the network).
GET /features/1 by user1
PUT /features/1 //done by user 2
PUT /features/1 //done by user1
the PUT by user2 will not be visible for user1 and user1 will make an update on the old object's state (with id=1) what can be done in this situation?
Conditional Requests.
You arrange things such that (a) the GET request from the server includes validators that identify the representation (b) the server responds 428 Precondition Required when the request lacks conditional headers (c) the clients know to read the validators from the resource metadata, and use the correct condition headers when submitting the PUT request (d) the server knows to compare the validator to the current representation before accepting the new representation.
Should PUT's and PATCH's URL contains ID or could it be put inside the body?
PUT /person/UUID {"name": "Jimmy"}
OR
PUT /person/{"UUID":1, "name": "Jimmy"}
( the same for the PATCH)
?
As PUT is defined as Replace the current document found at the URI location with the one provided in the payload sending a PUT request to /person should probably lead to a removal of any person managed by that particular endpoint, in case that URI represents a collection of persons.
As mentioned in this post one might use a URI without some special entity identifier in case this is an all purpose container. Think of a clipboard where you can copy some data to to later on retrieve it to paste it somewhere else. In such a case the identifier is implicitly given by the URI itself, as after all URI stands for unique resource identifier
Note that a URI as a whole identifies a resource and does not necessarily imply some form of parent-child structure. A client especially should not attempt to extract knowledge from an URI at all.
In regards to PATCH it depends. Usually one should use a media-type that is intended for patching such as JSON Patch or JSON Merge Patch.
The former representation defines certain fields that state that a field should be added, removed or replaced with a given value in a notation like the one listed below:
PATCH /my/data HTTP/1.1
Host: example.org
Content-Length: 326
Content-Type: application/json-patch+json
If-Match: "abc123"
[
{ "op": "test", "path": "/a/b/c", "value": "foo" },
{ "op": "remove", "path": "/a/b/c" },
{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] },
{ "op": "replace", "path": "/a/b/c", "value": 42 },
{ "op": "move", "from": "/a/b/c", "path": "/a/b/d" },
{ "op": "copy", "from": "/a/b/d", "path": "/a/b/e" }
]
JSON Merge Patch however works differently. It defines some default rules that instruct a server on how to apply the changes to the target document. A document like below i.e. will either add or update field a to have value z afterwards while the property f of c's object has to be deleted. All the other remaining properties of that resource remain as they are.
PATCH /target HTTP/1.1
Host: example.org
Content-Type: application/merge-patch+json
{
"a":"z",
"c": {
"f": null
}
}
Both of these media-types could be used to send a request directly to the "collection"-resource as both can target sub-elements by definition. However, in terms of caching I'd try to avoid it.
Caching in HTTP works de facto on the URI of the resource. Any unsafe operation performed on that URI leads to the cache invalidating a stored representation for that target. I.e. if you previously invoked GET /person/1 and now perform a PUT or PATCH, which both are unsafe operations, on /person the data might get updated, though a client requesting GET /person/1 afterwards may still retrieve the cached response via the cache as it is unaware of any changes done to that resource.
We have an article resource with properties:
title
image
description
status: published | draft
if we want only to remove image we make request
{title: null, image: null, description: null, status: null}
if we want only to update status we make request
{title: null, image: null, description: null, status: draft}
but in this case image also will be removed
How in REST to update only one property?
Performing partial modifications to a resource
The PATCH method can be used to perform partial modifications to a resource. The request payload should contain a set of instructions describing how the resource will be modified. See the following quote from the RFC 5789:
2. The PATCH Method
The PATCH method requests that a set of changes described in the request entity be applied to the resource identified by the Request-URI. [...]
The difference between the PUT and PATCH requests is reflected in the way the server processes the enclosed entity to modify the resource identified by the Request-URI. In a PUT request, the enclosed entity is considered to be a modified version of the resource stored on the origin server, and the client is requesting that the stored version be replaced. With PATCH, however, the enclosed entity contains a set of instructions describing how a resource currently residing on the origin server should be modified to produce a new version. [...]
To describe such set of instructions, you can use JSON Patch defined in the RFC 6902:
1. Introduction
JSON Patch is a format (identified by the media type application/json-patch+json) for expressing a sequence of operations to apply to a target JSON document; it is suitable for use with the HTTP PATCH method.
This format is also potentially useful in other cases in which it is necessary to make partial updates to a JSON document or to a data structure that has similar constraints [...]
Examples with JSON Patch
To update the status, you can do the following:
PATCH /articles/1 HTTP/1.1
Host: example.com
Content-Type: application/json-patch+json
[
{ "op": "replace", "path": "/status", "value": "draft" }
]
Use the following to remove the image:
PATCH /articles/1 HTTP/1.1
Host: example.com
Content-Type: application/json-patch+json
[
{ "op": "remove", "path": "/image" }
]
And use the following to update the status and remove the image:
PATCH /articles/1 HTTP/1.1
Host: example.com
Content-Type: application/json-patch+json
[
{ "op": "replace", "path": "/status", "value": "draft" },
{ "op": "remove", "path": "/image" }
]
Alternatively to JSON Patch, you may want to consider JSON Merge Patch defined in the RFC 7396: it's also a means of describing a set of modifications to a target resource's content.
Let me give an example which troubles me when using JSON-Patch message in PATCH message in a RESTFul API.
If we have a structure like:
{"books": [
{"title": "HTTP-Protocol", "image": "http.jpg", "description": "A standard book on HTTP", "status": "Available"},
{"title": "JSON-Patch", "image": "patching.jpg", "description": "Explanation on how to use json-patch in RESTFul", "status": "Planned"},
{"title": "RESTFul", "image": "fielding.jpg", "description": "Representational state transfer", "status": "Available"}
]}
Now the client who requested this information wants to change the status of the third book (RESTFul) from "Available" to "Borrowed".
Using JSON-Patch he would send the following message:
[
{
"op": "replace",
"path": "/books/2/status",
"value": "Borrowed"
}
]
Meaning that from the list, as he saw it, he wants to change the status of the third item. But since a RESTFul API call is stateless the server does not know anymore what list it gave to the client. Maybe in the mean time a new book was added and the list on the server change to:
{"books": [
{"title": "HTTP-Protocol", "image": "http.jpg", "description": "A standard book on HTTP", "status": "Available"},
{"title": "In between", "image": "destroy.jpg", "description": "Not RESTFul?", "status": "Available"},
{"title": "JSON-Patch", "image": "patching.jpg", "description": "Explanation on how to use json-patch in RESTFul", "status": "Planned"},
{"title": "RESTFul", "image": "fielding.jpg", "description": "Representational state transfer", "status": "Available"}
]}
Then this json-patch message received from the client will change the book with the title "JSON-Patch" and not the book with title "RESTFul" as intended by the client.
What is in your opinion the solution for this problem?
Following HATEOAS principles that each states should be hyperlinked, what is the best way to model links that change resource state?
Let's take classical example with orders:
{
id : 12,
state: 'pending',
...,
links: [
...,
{
rel: 'cancel',
href: '/orders/12/cancel'
},
...
]
}
I am not totall happy with that "/cancel" part - I would feel a lot better if I could send "PUT" request with contents:
{
status:'cancelled'
}
But how do I represent that with "href" attribute in links section? I would like to represent available actions there since, for example, cancelling an order isn't always possible ('completed' state).
One possibility would be to use URL like '/orders/12?action=cancel' what it kinda feels like RPC approach and that I am missing something.
Another possibility that looks probably nicest, would be to have links like that:
{
rel: 'cancel',
href: '/orders/12/',
type: 'PUT',
values: {
state: 'cancelled'
}
}
This solution maybe feels a little bit verbose.
Any ideas how to handle that gracefully? Maybe someone has already solved similar "problem"?
Modelling resources is the most difficult part of REST. Strictly adhering to the standard means if you see yourself ever doing this: /resource/:id/{action}, you're violating the "using HTTP correctly" criteria, as your endpoints should ideally always be "nouns', never "verbs" (the verbs are what the HTTP protocol provides).
So, while "it depends" (ie. the hard part of designing resources), typically:
Object model states can be considered as resources themselves.
Which means, your order status is actually a resource you can query (either as a standalone /orderstatuses resource or as a sub resource eg. /orders/:id/status)
Your Application State can now link to the status resource based on the current status of the order itself. If your 'status' schema looks something like this (pseudo):
key: 'status'
values: ['pending', 'cancelled']
Your app could then PUT /order/:id/status {status:'cancelled'} (a well formed status) back to the API, which would then act to cancel your order. It's a little weird thinking in these terms (RPC is a lot more intuitive), but hopefully this helps.
You have to describe forms somehow. You "verbose" solution is perfectly okay:
{
rel: 'cancel',
href: '/orders/12/',
type: 'PUT',
values: {
state: 'cancelled'
}
}
note: you have to define a custom MIME type or use a generic MIME type which is capable of describing forms (e.g. collection+json), or which an RDF type (which supports REST vocabs like Hydra) - aka. uniform interface / self-descriptive messages
I would like to represent available actions there since, for example,
cancelling an order isn't always possible ('completed' state).
If an operation is not available, then don't send a link pointing to that operation.
I would suggest either of these two models. The first is the classic one, but with rel="edit-form" and using PATCH where available. The second is an alternative which comes about through some lateral thinking about how the HTTP resource model maps onto your application domain model (namely, that the two don't have to have a 1:1 mapping).
Solution 1
Edit the resource in-place.
HTML compatible:
HTTP/1.1 200 OK
Content-Type: text/html
Location: /orders/1/
...<a rel="edit-form" href="./edit">Edit</a>...
HTTP/1.1 200 OK
Content-Type: text/html
Location: /orders/1/edit
...
<form action="../" method="POST">
<input type="hidden" name="_METHOD" value="PATCH">
<button type="submit" name="status" value="cancelled">Cancel Order</button>
</form>
...
POST /orders/1 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
_METHOD=PATCH&status=cancelled
Rich client (e.g. HTML+Javascript) compatible:
PATCH /orders/1 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
status=cancelled
and/or
PATCH /orders/1 HTTP/1.1
Content-Type: text/json
{
"status": "cancelled"
}
the _METHOD key is a well-known means of providing REST frameworks with the correct method due to HTML's lack of support for HTTP.
Solution 2
Or, Delete the resource (and, incidentally, create a new one)
DELETE /orders/1 HTTP/1.1
HTTP/1.1 201 Created
Location: /cancelled-orders/1
For more info on this way of mapping web resources to domain objects, please see my answer to a similar question.
Another answer you may wish to read is this one.
Further to papercowboy's answer, where status transitions are not always available you can document what is currently possible as a resource, e.g.
/order/:id/availableStates
{
"availableStates": [
{"status": "cancelled"}
]
}
I am trying to understand a little bit more about the REST ways :). Let's say I have a collection of ProductTypes that one can retrive via url GET /productTypes and let's say that we can but don't have to apply any filter. So this fetches 500 records.
Now if I want to do full update of the resource I could use PUT or POST at /productTypes/123. So far so good.
If I am creating a new resource and I know the id ahead of time, I would use PUT at /productTypes/123 and return 201 otherwise for update PUT with 200.
If I do partial update of the resource I would do PUT or POST at /productTypes/123/displayOrder and return 303 indicating that some other resource has been modified.
But what if I do 1 POST sending in basically a list of Key-Value pairs of product Id and display order, and I modify 5 out of 500 records. How do I indicate now that these 5 records have changed?
What happens with GET at /productTypes now after this partial update. I read somewhere that instead of returning multiple records with this get, I should return the list links to the resources, and then fetch them one by one, as that would allow for insane caching. But still how do I indicate that 5 records have changed? Do I need to do 5 separate posts with 303, or is there different mechanism. Hope this makes sense.
I don't see anything in the spec that specifically bars you from using multiple Content-Location headers. I tested it out and Chrome's totally OK with it. It won't automatically redirect for you, but you don't want that anyway with multiple resources involved.
HTTP/1.1 303 See Other
Content-Location: /productTypes/123/displayOrder
Content-Location: /productTypes/456/displayOrder
Content-Location: /productTypes/789/displayOrder
Content-Location: /productTypes/012/displayOrder
Content-Location: /productTypes/345/displayOrder
I'd do this:
PATCH /productTypes HTTP/1.1
Content-Type: application/json
{"item":[
{"id":"1","displayOrder":"4"},
{"id":"2","displayOrder":"2"},
{"id":"3","displayOrder":"1"},
{"id":"4","displayOrder":"3"},
{"id":"5","displayOrder":"5"}
]}
HTTP/1.1 200 OK
Content-Type: application/hal+json
{
"_links": { "self": {"href": "/productTypes" } }
"_embedded": {
"item": [{
"_links": { "self": { "href": "/productTypes/1" } },
"displayOrder": "4"
},
/* 4 more entries */
]
}
}