API Media Types

When the RBdigital API was being designed, one of the biggest concerns was how to manage change over time. One outcome of the attempt to gracefully handle adapting to changing client needs, was the introduction of API Media Types. API Media Types are not the same as 'Accept-Media' headers, which indicate acceptable formats for return data, but the concepts are somewhat related.

Instead of indicating the desired or acceptable format of the return data, the API Media Type allows the calling client to indicate a desired shape of the data. Although the following example is ficticious, it is instructive. For example, say a given endpoint allows a client to send an HTTP GET request to the following endpoint:

http://api.somecompany.com/v1/employees/{employeeId}

And the shape of the returned data was:

{
   "employeeId" : 12345,
   "lastName" : "Whitley",
   "firstName" : "Jonathan",
   "yearsWithCompany" : 9,
   "designation" : "CTO"
}

The shape of the data may be sufficient for the initial specifications. However, what if the need arises to supply additional information? For example, for HR purposes the employee salary needs to be included. What to do? Create another endpoint? Perhaps...

http://api.somecompany.com/v1/hr/employees/{employeeId}

In this case, we have increased the surface area of our API. A new endpoint for every use case is a certain way to cause future headaches. The API becomes "stringy" and brittle. It's a tempting choice to make because it's a simple one-off solution and there is often great pressure to get things out the door quickly.

We chose to solve the problem with API Media Types.

An API Media Type is a generalized name for the shape of the return data. In the RBdigital API, there are five named shapes:

There are no global constraints inferred by the names upon the actual shape of the domain objects returned by the API. Rather, when the shape of a particular domain object is considered, the number and selection of various domain object properties that are deemed "Basic" is quite arbitrary. The potential use cases for the domain object are considered, along with our experience with constructing applications using those domain objects.

Specific shape for Media Types might be as follows:


Basic

{
  "employeeId" : 12345,
  "name" : "Johnathan Witley",
  "designation" : "CTO"
}

Compact

{
   "employeeId" : 12345,
   "lastName" : "Whitley",
   "firstName" : "Johnathan",
   "yearsWithCompany" : 9,
   "designation" : "CTO"
}

Complete

{
   "employeeId" : 12345,
   "lastName" : "Whitley",
   "firstName" : "Johnathan",
   "yearsWithCompany" : 9,
   "designation" : "CTO",
   "phoneNumbers": [
      { "type" : "office",
	    "number" : "202-555-1212"
	  },
	  { "type" : "cell",
	    "number" : "312-456-0405"
	  }],
	"workEmail" : "jwhitley@somecompany.com"
}

Extended

{
   "employeeId" : 12345,
   "lastName" : "Whitley",
   "firstName" : "Johnathan",
   "yearsWithCompany" : 9,
   "designation" : "CTO",
   "phoneNumbers": [
      { "type" : "office",
	    "number" : "202-555-1212"
	  },
	  { "type" : "cell",
	    "number" : "312-456-0405"
	  }
	  ],
	"workEmail" : "jwhitley@somecompany.com",
	"salary" : 75000,
	"bonus" : 10000,
	"images" : [
		{ 
			"size" : "small",
			"url" : "http://somecompany.com/images/jwhitley_small.jpg"
		},
		{
			"size : "large",
			"url" : "http://somecompany.com/images/jwhitley_large.jpg"
		}
	]
}

Hypermedia

{
   "employeeId" : 12345,
   "lastName" : "Whitley",
   "firstName" : "Johnathan",
   "yearsWithCompany" : 9,
   "designation" : "CTO",
   "phoneNumbers": [
      { "type" : "office",
	    "number" : "202-555-1212"
	  },
	  { "type" : "cell",
	    "number" : "312-456-0405"
	  }
	  ],
	"workEmail" : "jwhitley@somecompany.com",
	"salary" : 75000,
	"bonus" : 10000,
	"images" : [
		{ 
			"size" : "small",
			"url" : "http://somecompany.com/images/jwhitley_small.jpg"
		},
		{
			"size : "large",
			"url" : "http://somecompany.com/images/jwhitley_large.jpg"
		}
	],
	"links" : [
		{
			"action" : "Grant Raise",
			"url" : "http://api.somecompany.com/v1/remuneration/pay-raise?amount={dollarAmount}"
			"httpVerb" : "POST"
		},
		{
			"action" : "Fire employee",
			"url" : "http://api.somecompany.com/v1/employees/12345"
			"httpVerb" : "DELETE"
		}
	
	]
}