A RESTful, hypertext-driven system needs to enable clients to create a new resource that depends on three or more resources of different types. What's the best method to expose this capability?
As an example, let's say I run an online store. The server knows about four resources:
When the Order is shipped, a client needs to record this event by creating a new Shipment on the server. The Shipment will require references to Destination, Order, and Packer.
To implement the creation of new Shipments, I can think of three approaches, and I don't like any of them:
I don't like Option 1 because the Shipment media type additionally defines links to the Order, Packer, and Destination. Here, a "link" is a JSON hash consisting of a human readable name, a URI, and a media type. Adding "order_uri", "packer_uri", and "destination_uri" to the media type doesn't seem very DRY because it duplicates the URIs for the associated resources.
Option 2 uses deeply-nested URIs, which neither look very maintainable nor capture any meaningful hierarchical information.
Option 3 places another level of abstraction between clients and the creation of Shipments, which makes the system harder to learn.
If a Shipment only depended on one other resource, Option 2 would make a lot more sense, but it doesn't in this case. As it stands, I favor Option 3, but would prefer something better.
In this example, what would be the best combination of URI and media type for creating a new Shipment? What other approaches should be considered?
Update: below is a JSON example representation of a Shipment resource showing links for order, packer, and destination. The URI duplication required by Option 1 appears in the "shipment" hash:
{
"shipment":{
"created_at": "Wed Sep 09 18:38:31 -0700 2009",
"order_uri":"http://example.com/orders/815",
"packer_uri":"http://example.com/packers/42",
"destination_uri":"http://example.com/destinations/666"
},
"order":{
"name":"the order to which this shipment belongs",
"uri":"http://example.com/orders/815",
"media_type":"application/vnd.com.example.store.Order+json"
},
"packer":{
"name":"the person who packed this shipment",
"uri":"http://example.com/packers/42",
"media_type":"application/vnd.com.example.store.Packer+json"
},
"destination":{
"name":"the destination of this shipment",
"uri":"http://example.com/destinations/666",
"media_type":"application/vnd.com.example.store.Destination+json"
}
}
The contents of the "shipment" hash (less "created_at" field) would get POSTed. When using GET, the full Shipment representation above would be be sent.
REST architecture treats every content as a resource. These resources can be Text Files, Html Pages, Images, Videos or Dynamic Business Data. REST Server simply provides access to resources and REST client accesses and modifies the resources. Here each resource is identified by URIs/ Global IDs.
Resources are typically created by sending a POST request to the parent collection resource. This creates a new subordinate resources with a newly generated id. For example, a POST request to /projects might be used to create a new project resource at /projects/123.
According to the RFC 2616 standard, the POST method should be used to request that the server accept the enclosed entity as a subordinate of the existing resource identified by the Request-URI. This means the POST method call will create a child resource under a collection of resources.
In this case, we refer to these resources as singleton resources. Collections are themselves resources as well. Collections can exist globally, at the top level of an API, but can also be contained inside a single resource. In the latter case, we refer to these collections as sub-collections.
REST "hierarchies" don't mean anything. They're a convenience for navigation to show a relationship in the form of a path. Not a hierarchy per se, but a path. So, option 2 is actually sensible if you drop the "hierarchy" concept and recognize that there are many alternative paths to the same final location.
Your option 2 is a orders->packers->destination path. Theoretically, orders->destinations->packers and packers->orders->destinations, packers->destinations->orders, as well as a few others all lead the same place. Yes, it's a pain to support them all. However, it's proof that all of them are equivalent and there's no hierarchy.
"I don't like Option 1 because [it] doesn't seem very DRY."
So? Leave out the repetitive stuff. Why does a shipment have to also contain a complete repeat of the Order and Packer information? The URI references are sufficient to allow a lookup and retrieve Order and Packer. Why send Order and Packer at all?
"Option 3 makes the system harder to learn." For whom? Developers? You're designing your system around the developers, not the users and their use case? For shame.
The point of REST is (generally) that a URI is an absolute, final and eternal thing. Which alternative gives you the absolutely best URI structure? Recognize that URI's are not hierarchies but paths -- and objects can exist at the end of multiple alternative paths.
You're creating a shipment. POST to /shipment
. Simple, clear URI's are what matter.
Ok, now I understand where you are seeing duplication. Would it be feasible to POST the following?
{
"shipment":{
"created_at": "Wed Sep 09 18:38:31 -0700 2009",
"order":{
"uri":"http://example.com/orders/815"
},
"packer":{
"uri":"http://example.com/packers/42",
}
"destination":{
"uri":"http://example.com/destinations/666",
}
}
}
and return this
{
"shipment":{
"created_at": "Wed Sep 09 18:38:31 -0700 2009",
"order":{
"name":"the order to which this shipment belongs",
"uri":"http://example.com/orders/815",
"media_type":"application/vnd.com.example.store.Order+json"
},
"packer":{
"name":"the person who packed this shipment",
"uri":"http://example.com/packers/42",
"media_type":"application/vnd.com.example.store.Packer+json"
},
"destination":{
"name":"the destination of this shipment",
"uri":"http://example.com/destinations/666",
"media_type":"application/vnd.com.example.store.Destination+json"
}
}
}
Maybe this just does not work in JSON, but I do something similar with XML in my resources. The idea is that you can pass to the server a "reference" to a resource with just the uri filled in and the server populates the rest of the data in the object.
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