OCAPI Resource States 23.2

What is a Resource State?

A resource state represents the server-side state of a specific resource e.g. a basket or a customer. It is a string token, baked on all the resource property information.

OCAPI exposes resource state information via the _resource_state property in the response payload (body). The response contains either a single resource state or multiple resource states in case calling collection or search resources. In case of body-less responses (e.g. HEAD) the resource state is exposed via x-dw-resource-state response header.

What is it used for?

Resource states provide an OPTIONAL mechanism to implement Optimistic Locking in your client. They can be used to prevent collisions between concurrent requests e.g. to overcome the "lost update" problem.

Resource State vs. Etag

A strong Etag covers not only the server-side resource state itself, but also the whole HTTP response on bit level. Means, it covers also the fact that compression (e.g. gzip) is used or not. In scenarios where proxies are in between this might lead to unforeseen behaviours. E.g in case the client doesn't request compression, but there is compression between the proxy and the server: Some proxies append "-gzip" to the Etag value, others absorb the Etag completely.

Note: OCAPI uses the term "resource_state" to clearly differentiate the concept from Etags. OCAPI is not supporting Etags anymore.

How does it work?

To use the resource state for optimistic locking you have to include the resource state from the last server response in your next state changing (DELETE, PATCH, POST, PUT) request. You should always prefer to use the _resource_state property in the body. In case the requested API doesn't have a body use the x-dw-resource-state header. Whenever a resource state is part of a client's request, OCAPI verifies it by comparing the given value with the server resource state. If both resource states are equal the operation is executed, otherwise an HTTP 409 ResourceStateConflictException fault is returned. In case of a create request (e.g. with PUT) where the resource is not expected to be existing (with not_exists state), the returned fault is an HTTP 409 ResourceAlreadyExistsException instead.

Note: Whenever your application performes state changing (DELETE, PATCH, POST, PUT) operations and there is a likelihood of concurrent requests, we recommend to leverage optimistic locking on base of resource states.

Samples

OCAPI exposes the resource state as _resource_state property in response body and as x-dw-resource-state header.

# Create a new basket in the Shop API:
REQUEST:
POST /dw/shop/v23_2/baskets
Host: example.com
Authorization:Bearer eyJfdiI6IjXXXXXX.eyJfdiI6IjEiLCJleHAXXXXXXX.-d5wQW4c4O4wt-Zkl7_fiEiALW1XXXX
Content-Type: application/json

# in case of success, response contains new created basket with the resource state in response
# header and also as single property in response body
RESPONSE:
HTTP/1.1 200 OK
Content-Length:124
x-dw-resource-state:ba4e84383e1790597e49eeee34b201633d80ed3f499992f5af11d639dd903a36
Content-Type:application/json;charset=UTF-8
{
   "_resource_state" : "ba4e84383e1790597e49eeee34b201633d80ed3f499992f5af11d639dd903a36",
   ...
   "basket_id" : "bczFTaOjgEqUkaaadkvHwbgrP5",
   ...
}

# Search for stores in the Data API:
REQUEST:
POST /s/-/dw/data/v23_2/sites/SiteGenesis/store_search HTTP/1.1
Host: example.com
Authorization: Bearer a5b6eb0d-8312-41a3-88f3-2c53c4507367
...

# in case of success, response contains multiple resource states - one per search hit
RESPONSE:
HTTP/1.1 200 OK
{
   ...
   "hits" : [{
      "_resource_state" : "12ab34cd",
      "id" : ...
   },{
      "_resource_state" : "ef56gh78",
      "id" : ...
   }]
}

A resource state can be passed to any state changing method (DELETE, PATCH, POST, PUT). If no resource state is passed, the resource will be overwritten, updated or deleted without any further notice on base of the default HTTP method behaviour. If a resource state is passed, it is verified against the server resource state. If both states do not match, a ResourceStateConflictException or ResourceAlreadyExistsException fault is responded. When the API user expects a resource not to exist (e.g. during resource creation), the resource state not_exists can be passed. With that already existing resources are not accidentally overwritten.

For every writing operation on a resource, a resource state token can be passed, but does not have to. If no token is passed, the resource will be overwritten, updated or deleted without any further notice. If a resource state is passed, it is checked against the server state of the resource. If both states do not match, a 409 status code is retrieved. When the API user expects a resource not to exist, a resource state not_exists must be passed (in case of a create request).

# add an item to the basket, the resource state from the response header is given as x-dw-resource-state
# header ...
REQUEST:
POST /dw/shop/v23_2/baskets/bczFTaOjgEqUkaaadkvHwbgrP5/items?client_id=af123-456....
Host: example.com
Authorization:Bearer eyJfdiI6IjXXXXXX.eyJfdiI6IjEiLCJleHAXXXXXXX.-d5wQW4c4O4wt-Zkl7_fiEiALW1XXXX
x-dw-resource-state:ba4e84383e1790597e49eeee34b201633d80ed3f499992f5af11d639dd903a36
Content-Type: application/json
{
...
}

# ... or as _resource_state property in the body

# Add an item to the basket, send resource state via body

REQUEST:
POST /dw/shop/v23_2/baskets/bczFTaOjgEqUkaaadkvHwbgrP5/items
Host: example.com
Authorization: Bearer eyJfdiI6IjXXXXXX.eyJfdiI6IjEiLCJleHAXXXXXXX.-d5wQW4c4O4wt-Zkl7_fiEiALW1XXXX
Content-Type: application/json
{
   "_resource_state" : "ba4e84383e1790597e49eeee34b201633d80ed3f499992f5af11d639dd903a36"
   ...
}

# in case of success, the next response header contains a new resource state
RESPONSE:
HTTP/1.1 200 OK
Content-Length:124
x-dw-resource-state: ba4e84383e1790597e49eeee34b201633d80ed3f499992f5af11d639dd903a36
Content-Type:application/json;charset=UTF-8
{
   "_resource_state" : "ba4e84383e1790597e49eeee34b201633d80ed3f499992f5af11d639dd903a36"
   ...
   "basket_id" : "bczFTaOjgEqUkaaadkvHwbgrP5",
   ...
}

# Delete an item from the basket, resource state is send via header

REQUEST:
DELETE /dw/shop/v23_2/baskets/bczFTaOjgEqUkaaadkvHwbgrP5/items/te352d63gdh62hd6hd
Host: example.com
Authorization: Bearer eyJfdiI6IjXXXXXX.eyJfdiI6IjEiLCJleHAXXXXXXX.-d5wQW4c4O4wt-Zkl7_fiEiALW1XXXX
x-dw-resource-state: 100001
Content-Length: 0

# in case of creating a not existent customer

REQUEST:
PUT /s/-/dw/data/v23_2/customer_lists/4711/customers/0815 HTTP/1.1
Host: example.com
Authorization: Bearer a5b6eb0d-8312-41a3-88f3-2c53c4507367
Content-Type: application/json; charset=UTF-8
{
   "_resource_state" : "not_exists",
   "email" : "[email protected]",
   ...
}

# when a customer with that ID already exists

RESPONSE:
HTTP/1.1 409 CONFLICT
Content-Type: application/json;charset=UTF-8
{
  "_v" : "23.2",
   "_type" : "fault",
  "fault" : {
    "type" : "ResourceAlreadyExistsException",
    "message" : "Resource already exists, state is 'ab12cd34'."
  }
}
X OCAPI versions 15.x and 16.x will be retired on March 31, 2021. For dates and more information, see the OCAPI versioning and deprecation policy and this Knowledge Article.