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:
- Order: The group of products to be shipped. [has one Shipment]
- Destination: The location to ship to. [has many Shipments]
- Shipment: The act of sending a Product to a Customer. [belongs to Destination, Order, and Packer]
- Packer: The employee physically preparing the Order for Shipment. [has many Shipments]
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:
- POST to /shipments using the Shipment media type. The Shipment media type has three fields: "order_uri"; "packer_uri"; and "destination_uri". Each URI is being used as a unique identifier for the Order, Packer, and Destination involved in the Shipment, respectively.
- POST to /orders/{order_id}/packers/{packer_id}/destinations/{destination_id}/shipments using the Shipment media type.
- Add a new resource to the system called "ShipmentBuilder". POST to /shipment_builders using "packer_uri", "destination_uri", and "order_uri" contained within a ShipmentBuilder media type.
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.