Hypermedia Linking

RESTful APIs use HTTP as their native protocol, with resources residing at unique URIs. Linking to related resources by passing those URIs within the response allows a well-designed client to discover functionality without relying on the documentation. It also allows some flexibility in the ability of the server to provide a URI structure that better reflects the application without requiring the client to hardcode many base URLs or adhere to byzantine URI structuring rules.

The links returned for each call should be related to what can be done next for each resource. For example, if the response is showing a list of resources, such as could be called by the endpoint

/categories/39/products

The response would include a link to each returned resource as well as links that show the client how to add new items to the collection and, if pagination is applied, links to the previous and next groups of the collection. The response in this case would resemble:

{
	"data":[{
		"products": [
		 	{
               "product_id": "76124",
               "name": "The Amazing Widget",
               "weight": "7.2 oz",
               ...
			   "_links": [
			   		{
						"rel": "self",
						"href": "http://api.example.com/v1/products/76124",
						"method": "GET"
					},
					{
						"rel": "http://api.example.com/v1/categories/definition",
						"href": "http://api.example.com/v1/categories/39",
						"method": "GET"
					},
					{
						"rel": "http://api.example.com/v1/mycart/definition#add_to_cart",
						"href": "http://api.example.com/v1/mycart",
						"method": "POST",
						"parameters": {
							"product": 76124,
							"quantity": 1
						}
					}
			   ]
          	},
		 	{
               "product_id": "84567",
               "name": "Rubber Ducky",
               "weight": "4.6 oz",
               ...
			   "_links": [
			   		{
						"rel": "self",
						"href": "http://api.example.com/v1/products/84567",
						"method": "GET"
					},
					{
						"rel": "http://api.example.com/v1/categories/definition",
						"href": "http://api.example.com/v1/categories/39",
						"method": "GET"
					},
					{
						"rel": "http://api.example.com/v1/mycart/definition#add_to_cart",
						"href": "http://api.example.com/v1/mycart",
						"method": "POST",
						"parameters": {
							"product": 84567,
							"quantity": 1
						}
					}
			   ]
          	},
		 	{
               "product_id": "76097",
               "name": "Whoopie Cushion",
               "weight": "2.2 oz",
               ...
			   "_links": [
			   		{
						"rel": "self",
						"href": "http://api.example.com/v1/products/76097",
						"method": "GET"
					},
					{
						"rel": "http://api.example.com/v1/categories/definition",
						"href": "http://api.example.com/v1/categories/39",
						"method": "GET"
					},
					{
						"rel": "http://api.example.com/v1/mycart/definition#add_to_cart",
						"href": "http://api.example.com/v1/mycart",
						"method": "POST",
						"parameters": {
							"product": 76097,
							"quantity": 1
						}
					}
			   ]
          	},
		],
		"_links":
		[
			{
				"rel": "next",
				"href": "http://api.example.com/v1/categories/39/products?offset=11&limit=10",
				"method": "GET"
			}
		]
	}]
}

The “href” value is the address for the link itself and should always return a fully qualified URI, including the protocol string (i.e. “http://”) and the host name. The “method” value indicates the HTTP method the client should use to properly access the URI as defined by the relationship type. Everything the client needs to identify the next steps it can take should be found in the hypermedia links.  The “parameters” value – which is optional – indicates what parameters should be included with the call.

The “rel” value for each link indicates the relation type of the link. For relation types registered with the IANA, just the relation string is sufficient. For custom relations that point to other resources in the application, this value should be a URI that points to the appropriate definition of the resource. In most cases, this URI should point to the definition link for the resource. However, this method of relationship linking also opens the possibility of using standards set elsewhere on the web, such as schema.org.

8 thoughts on “Hypermedia Linking”

  1. Your link to the IANA is broken. Looks like you’ve got an extra space in the URL between the last dash and the term “relations”.

    1. The parameters list coming back from a response should only contain the current state of the resource, not what can be done to it. That’s where resource definitions and descriptor files come in. For example, IO Docs allows you to define an enum for a given parameter that could contain all possible colors.

      In your case, though, I can also see how, if we’re talking about a Product resource, relying on the descriptor won’t work as different individual products will have different color options. So, I’d include a parameter in the response named something like “available_colors” that would contain an array of the colors the product is available in. That still hold with the constraint that the resource is responsible for its state. For example, if you’re selling a shirt that comes in black, white or red and you recently added ash as a color, you simply add ash to that array and subsequent calls to the Products resource with the ID of that shirt will contain the complete set of color choices.

      As for how to indicate that in an order, your users are unlikely to change the Products resource. They may, instead, add it to a “ShoppingCart” resource and, under the “options” parameter (which could be a hash), may specify the “color”.

Leave a Reply

Your email address will not be published. Required fields are marked *