Editing or updating a resource is a common purpose for APIs. Edits can be achieved by sending either POST
, PUT
or PATCH
requests to the respective resource. Although POST
is allowed to append data to a resource's existing representation it is recommended to use either PUT
or PATCH
as they convey a more explicit semantic.
Your server should respond with 200 OK
if the update was performed, or 202 Accepted
if it has yet to be applied. Pick the most appropriate error code if it cannot be completed.
PUT
has the semantics of replacing the current representation with the payload included in the request. If the payload is not of the same representation type as the current representation of the resource to update, the server can decide which approach to take. RFC7231 defines that the server can either
415 Unsupported Media Type
response indicating that the target resource is limited to a specific (set) of media types.A base resource containing a JSON HAL representation like ...
{
"name": "Charlie Smith",
"age": 39,
"job_title": "Software Developer",
"_links": {
"self": { "href": "/users/1234" },
"employee": { "href": "http://www.acmee.com" },
"curies": [{ "name": "ea", "href": "http://www.acmee.com/docs/rels/{rel}", templated": true}],
"ea:admin": [
"href": "/admin/2",
"title": "Admin"
]
}
}
... may receive an update request like this
PUT /users/1234 HTTP/1.1
Host: http://www.acmee.com
Content-Type: "application/json; charset=utf-8"
Content-Length: 85
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
{
"name": "Charlie Gold-Smith",
"age": 40,
"job_title": "Senior Software Developer"
}
The server may now replace the state of the resource with the given request body and also change the content-type from application/hal+json
to application/json
or convert the JSON payload to a JSON HAL representation and then replace the content of the resource with the transformed one or reject the update request due to an inaplicable media type with a 415 Unsupported Media Type
response.
There is a difference between replacing the content directly or first transforming the representation to the defined representation model and then replacing the existing content with the transformed one. A subsequent GET
request will return the following response on a direct replacement:
GET /users/1234 HTTP/1.1
Host: http://www.acmee.com
Accept-Encoding: gzip, deflate
Accept-Language: en-us
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
ETag: "e0023aa4e"
{
"name": "Charlie Gold-Smith",
"age": 40,
"job_title": "Senior Software Developer"
}
while the transformation and then replace approach will return the following representation:
GET /users/1234 HTTP/1.1
Host: http://www.acmee.com
Accept-Encoding: gzip, deflate
Accept-Language: en-us
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
ETag: e0023aa4e
{
"name": "Charlie Gold-Smith",
"age": 40,
"job_title": "Senior Software Developer",
"_links": {
"self": { "href": "/users/1234" },
"employee": { "href": "http://www.acmee.com" },
"curies": [{ "name": "ea", "href": "http://www.acmee.com/docs/rels/{rel}", templated": true}],
"ea:admin": [
"href": "/admin/2",
"title": "Admin"
]
}
}
Note that PUT
is allowed to have side-effects although it is defined as idempotent operation! This is documented in RFC7231 as
A PUT request applied to the target resource can have side effects on other resources. For example, an article might have a URI for identifying "the current version" (a resource) that is separate from the URIs identifying each particular version (different resources that at one point shared the same state as the current version resource). A successful PUT request on "the current version" URI might therefore create a new version resource in addition to changing the state of the target resource, and might also cause links to be added between the related resources.
Producing additional log entries is not considered as side effect usually as this is certainly no state of a resource in general.
RFC7231 mentions this regarding partial updates:
Partial content updates are possible by targeting a separately identified resource with state that overlaps a portion of the larger resource, or by using a different method that has been specifically defined for partial updates (for example, the PATCH method defined in RFC5789).
Partial updates can therefore be performed in two flavors:
PUT
PATCH
and instruct the server what to updateIf a user representation needs to be partially updated due to a move of a user to an other location, instead of updating the user directly, the related resource should be updated directly which reflects to a partial update of the user representation.
Before the move a user had the following representation
GET /users/1234 HTTP/1.1
Host: http://www.acmee.com
Accept: application/hal+json; charset=utf-8
Accept-Encoding: gzip, deflate
Accept-Language: en-us
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
ETag: "e0023aa4e"
{
"name": "Charlie Gold-Smith",
"age": 40,
"job_title": "Senior Software Developer",
"_links": {
"self": { "href": "/users/1234" },
"employee": { "href": "http://www.acmee.com" },
"curies": [{ "name": "ea", "href": "http://www.acmee.com/docs/rels/{rel}", templated": true}],
"ea:admin": [
"href": "/admin/2",
"title": "Admin"
]
},
"_embedded": {
"ea:address": {
"street": "Terrace Drive, Central Park",
"zip": "NY 10024"
"city": "New York",
"country": "United States of America",
"_links": {
"self": { "href": "/address/abc" },
"google_maps": { "href": "http://maps.google.com/?ll=40.7739166,-73.970176" }
}
}
}
}
As the user is moving to a new location she updates her location information like this:
PUT /address/abc HTTP/1.1
Host: http://www.acmee.com
Content-Type: "application/json; charset=utf-8"
Content-Length: 109
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
{
"street": "Standford Ave",
"zip": "CA 94306",
"city": "Pablo Alto",
"country": "United States of America"
}
With the transformation-before-replace semantic for the mismatched media-type between the existing address resource and the one in the request, as described above, the address resource is now updated which has the effect that on a subsequent GET
request on the user resource the new address for the user is returned.
GET /users/1234 HTTP/1.1
Host: http://www.acmee.com
Accept: application/hal+json; charset=utf-8
Accept-Encoding: gzip, deflate
Accept-Language: en-us
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
ETag: "e0023aa4e"
{
"name": "Charlie Gold-Smith",
"age": 40,
"job_title": "Senior Software Developer",
"_links": {
"self": { "href": "/users/1234" },
"employee": { "href": "http://www.acmee.com" },
"curies": [{ "name": "ea", "href": "http://www.acmee.com/docs/rels/{rel}", templated": true}],
"ea:admin": [
"href": "/admin/2",
"title": "Admin"
]
},
"_embedded": {
"ea:address": {
"street": "Standford Ave",
"zip": "CA 94306",
"city": "Pablo Alto",
"country": "United States of America"
"_links": {
"self": { "href": "/address/abc" },
"google_maps": { "href": "http://maps.google.com/?ll=37.4241311,-122.1524475" }
}
}
}
}
PATCH
is defined in RFC5789 and is not directly part of the HTTP spec per se. A common misbelief is, that sending only the fields that should be partially updated is enough within a PATCH
request. The specification therefore states
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 set of changes is represented in a format called a "patch document" identified by a media type.
This means that a client should calculate the necessary steps needed to transform the resource from state A to state B and send these instructions to the server.
A popular JSON based media-type for patching is JSON Patch.
If the age and the job-title of our sample user changes and an additional field representing the income of the user should be added a partial update using PATCH
using JSON Patch may look like this:
PATCH /users/1234 HTTP/1.1
Host: http://www.acmee.com
Content-Type: application/json-patch+json; charset=utf-8
Content-Length: 188
Accept: application/json
If-Match: "e0023aa4e"
[
{ "op": "replace", "path": "/age", "value": 40 },
{ "op": "replace", "path": "/job_title", "value": "Senior Software Developer" },
{ "op": "add", "path": "/salery", "value": 63985.00 }
]
PATCH
may update multiple resources at once and requires to apply the changes atomically which means either all changes have to be applied or none at all which puts transactional burden on the API implementor.
A successful update may return something like this
HTTP/1.1 200 OK
Location: /users/1234
Content-Type: application/json
ETag: "df00eb258"
{
"name": "Charlie Smith",
"age": 40,
"job_title": "Senior Software Developer",
"salary": 63985.00
}
though is not restricted to 200 OK
response codes only.
To prevent in-between updates (changes done in-between the previous fetch of the representation state and the update) ETag
, If-Match
or If-Unmodified-Since
header should be used.
The spec on PATCH
recommends the following error handling:
Type | Error Code |
---|---|
Malformed patch document | 400 Bad Request |
Unsupported patch document | 415 Unsupported Media Type |
Unprocessable request, i.e. if the resoure would become invalid by applying the patch | 422 Unprocessable Entity |
Resource not found | 404 Not Found |
Conflicting state, i.e. a rename (move) of a field which does not exist | 409 Conflict |
Conflicting modification, i.e. if the client uses a If-Match or If-Unmodified-Since header which validation failed. If no precondition was available, the latter error code should be returned | 412 Precondition Failed or 409 Conflict |
Concurrent modification, i.e. if the request needs to be applied before acception further PATCH requests | 409 Conflict |