Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

REST: How to Create a Resource That Depends on Three or More Resources of Different Types?

Tags:

rest

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:

  1. 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.
  2. POST to /orders/{order_id}/packers/{packer_id}/destinations/{destination_id}/shipments using the Shipment media type.
  3. 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.

like image 351
Rich Apodaca Avatar asked Sep 09 '09 23:09

Rich Apodaca


People also ask

What are the different types of REST resource?

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.

How do I create a REST resource?

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.

Which RESTful method is used to create a resource on the server?

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.

What is the recommended term used to refer to multiple resources in API?

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.


2 Answers

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.

like image 52
S.Lott Avatar answered Oct 07 '22 21:10

S.Lott


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.

like image 36
Darrel Miller Avatar answered Oct 07 '22 21:10

Darrel Miller