A resource state represents the server-side state of a specific resource, for example, a basket or a customer. It’s 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 (for example, 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, for example, 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 (for example, 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’s compression between the proxy and the server: Some proxies append “-gzip” to the Etag value, others absorb the Etag completely.
OCAPI uses the term “resource_state” to clearly differentiate the concept from Etags. OCAPI isn’t supporting Etags anymore.
Note
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 (for example, with PUT) where the resource isn’t expected to be existing (with not_exists state), the returned fault is an HTTP 409 ResourceAlreadyExistsException instead.
Whenever your application performs state changing (DELETE, PATCH, POST, PUT) operations and there’s a likelihood of concurrent requests, we recommend leveraging optimistic locking on base of resource states.
Note
Samples
OCAPI exposes the resource state as _resource_state property in response body and as x-dw-resource-state header.
1# Create a new basket in the Shop API:2REQUEST:3POST /dw/shop/v24_5/baskets4Host: example.com5Authorization:Bearer eyJfdiI6IjXXXXXX.eyJfdiI6IjEiLCJleHAXXXXXXX.-d5wQW4c4O4wt-Zkl7_fiEiALW1XXXX6Content-Type: application/json78# in case of success, response contains new created basket with the resource state in response9# header and also as single property in response body10RESPONSE:11HTTP/1.1 200 OK12Content-Length:12413x-dw-resource-state:ba4e84383e1790597e49eeee34b201633d80ed3f499992f5af11d639dd903a3614Content-Type:application/json;charset=UTF-815{16 "_resource_state" : "ba4e84383e1790597e49eeee34b201633d80ed3f499992f5af11d639dd903a36",17 ...18 "basket_id" : "bczFTaOjgEqUkaaadkvHwbgrP5",19 ...20}2122# Search for stores in the Data API:23REQUEST:24POST /s/-/dw/data/v24_5/sites/SiteGenesis/store_search HTTP/1.125Host: example.com26Authorization: Bearer a5b6eb0d-8312-41a3-88f3-2c53c450736727...2829# in case of success, response contains multiple resource states - one per search hit30RESPONSE:31HTTP/1.1 200 OK32{33 ...34 "hits" : [{35 "_resource_state" : "12ab34cd",36 "id" : ...37 },{38 "_resource_state" : "ef56gh78",39 "id" : ...40 }]41}
A resource state can be passed to any state changing method (DELETE, PATCH, POST, PUT). If no resource state is passed, the resource is overwritten, updated, or deleted without any further notice on base of the default HTTP method behavior. If a resource state is passed, it’s verified against the server resource state. If both states don’t match, a ResourceStateConflictException or ResourceAlreadyExistsException fault is responded. When the API user expects a resource not to exist (for example, during resource creation), the resource state not_exists can be passed. This prevents existing resources from being accidentally overwritten.
For every writing operation on a resource, a resource state token can be passed, but doesn’t have to. If no token is passed, the resource is overwritten, updated, or deleted without any further notice. If a resource state is passed, it’s checked against the server state of the resource. If both states don’t 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 (for a create request).
1# add an item to the basket, the resource state from the response header is given as x-dw-resource-state2# header ...3REQUEST:4POST /dw/shop/v24_5/baskets/bczFTaOjgEqUkaaadkvHwbgrP5/items?client_id=af123-456....5Host: example.com6Authorization:Bearer eyJfdiI6IjXXXXXX.eyJfdiI6IjEiLCJleHAXXXXXXX.-d5wQW4c4O4wt-Zkl7_fiEiALW1XXXX7x-dw-resource-state:ba4e84383e1790597e49eeee34b201633d80ed3f499992f5af11d639dd903a368Content-Type: application/json9{10...11}1213# ... or as _resource_state property in the body1415# Add an item to the basket, send resource state via body1617REQUEST:18POST /dw/shop/v24_5/baskets/bczFTaOjgEqUkaaadkvHwbgrP5/items19Host: example.com20Authorization: Bearer eyJfdiI6IjXXXXXX.eyJfdiI6IjEiLCJleHAXXXXXXX.-d5wQW4c4O4wt-Zkl7_fiEiALW1XXXX21Content-Type: application/json22{23 "_resource_state" : "ba4e84383e1790597e49eeee34b201633d80ed3f499992f5af11d639dd903a36"24 ...25}2627# in case of success, the next response header contains a new resource state28RESPONSE:29HTTP/1.1 200 OK30Content-Length:12431x-dw-resource-state: ba4e84383e1790597e49eeee34b201633d80ed3f499992f5af11d639dd903a3632Content-Type:application/json;charset=UTF-833{34 "_resource_state" : "ba4e84383e1790597e49eeee34b201633d80ed3f499992f5af11d639dd903a36"35 ...36 "basket_id" : "bczFTaOjgEqUkaaadkvHwbgrP5",37 ...38}3940# Delete an item from the basket, resource state is send via header4142REQUEST:43DELETE /dw/shop/v24_5/baskets/bczFTaOjgEqUkaaadkvHwbgrP5/items/te352d63gdh62hd6hd44Host: example.com45Authorization: Bearer eyJfdiI6IjXXXXXX.eyJfdiI6IjEiLCJleHAXXXXXXX.-d5wQW4c4O4wt-Zkl7_fiEiALW1XXXX46x-dw-resource-state: 10000147Content-Length: 04849# in case of creating a not existent customer5051REQUEST:52PUT /s/-/dw/data/v24_5/customer_lists/4711/customers/0815 HTTP/1.153Host: example.com54Authorization: Bearer a5b6eb0d-8312-41a3-88f3-2c53c450736755Content-Type: application/json; charset=UTF-856{57 "_resource_state" : "not_exists",58 "email" : "dude@salesforce.com",59 ...60}6162# when a customer with that ID already exists6364RESPONSE:65HTTP/1.1 409 CONFLICT66Content-Type: application/json;charset=UTF-867{68 "_v" : "24.5",69 "_type" : "fault",70 "fault" : {71 "type" : "ResourceAlreadyExistsException",72 "message" : "Resource already exists, state is 'ab12cd34'."73 }74}
j
Attention!
The Open Commerce API (OCAPI) is now deprecated. The provisions described in our versioning and deprecation policy fully apply. For all new projects and major refactoring work, use B2C Commerce API (SCAPI) as the default REST API. For additional details, refer to Why Use SCAPI Instead of OCAPI.
DID THIS ARTICLE SOLVE YOUR ISSUE? Let us know so we can improve!