Apigee Web Api Design The Missing Link Ebook 1 4
Apigee Web Api Design The Missing Link Ebook 1 4
If your
web APIs do not include links today, a first step is simply to add some links without making other changes,
like this:
{“id”: “12345678”,
“kind”: “Dog”
“name”: “Lassie”,
“furColor”: “brown”,
“ownerID”: “98765432”,
ownerLink”: “https://dogtracker.com/persons/98765432”
{“id”:”98765432”,
“kind”: “Person”
“hairColor”: “brown”
{“id”:”98765432”,
“kind”: “Person”
“hairColor”: “brown”,
“dogsLink”: “https://dogtracker.com/persons/98765432/dogs”
16
Why is this better?
In the original example, if you had the representation of Lassie, and you wanted to get the representation
of Lassie’s owner, Joe, you would have to look in the documentation of the API to find a suitable URI
template you could use to construct a URL to locate Joe. In the representation of Lassie, you have an
ownerID, and you’re looking for a URL template that takes an ownerID as a variable and produces
a URL that locates an owner. This approach has several drawbacks. One is that it requires you to go
hunting in the documentation rather than just looking at the data. Another is that there isn’t really a
good documentation convention for describing which property values found in representations can
be plugged into which templates, so some amount of guesswork is usually necessary, even given the
right documentation. In this example, you would have to know that owners are people, and look for
templates that take the ID of a person as a variable. A third drawback is that you have to write code
that will combine the ownerID property value with the template and produce a URL. The URI template
specification calls such code a template processor. For simple templates, this code is fairly trivial, but it
still has to be written and debugged.
By contrast, in our modified design, there is less to learn, and using the API is easier. Joe’s URL is right
there in the data for you to see, so you don’t have to go digging through the documentation for anything.
Your client code is also easier to write—simply pick up the value of ownerLink and use it in an HTTP
request. You don’t have to write code to construct a URL from a template and a variable value, and
you can even write and use general-purpose code that follows these links without having any built-in
knowledge of the API. Adding dogsLink to the owner resource provides even more value. Without
dogsLink, it is not obvious from the data that it is even possible to navigate from an owner to their dogs.
An increasing number of APIs from major companies on the web are including links in their data. We’ll
share some examples from Google and GitHub in the Who uses links? section.
17
URI templates are most useful if they accept variables whose values are easy for humans to read and
remember. For example, the URL template
https://dogtracker.com/persons/{personID}
has some utility, but remembering the personID can be almost as hard as remembering the whole URL if
personID is some obscure and lengthy identifier. By contrast, the following template
https://dogtracker.com/persons/{personName}
is more useful because names are very fundamental to the way in which humans deal with the world.
This is the same reason that domain names exist to map names to IP addresses. On the web, when you
follow a link to the server that runs the Google Cloud blog, for example, you don’t care whether the link
contains a domain name or an IP address—you just use it. Having the domain name blog.apgiee.com does
improve the experience if you are typing the URL6.
6
Of course, domain names are also important for separating the logical web from the physical infrastructure, even for links.
18
One way to think about the URI templates of a typical web API is that they are analogous to Google
searches. They allow you to locate resources whose URL you don’t know using bits of information from
those resources that you do know. Most people know how to perform a Google search through a web
browser using URLs like this one:
https://www.google.com/search?q=web+api+design
When web API designers define URL templates that look like this
https://dogtracker.com/persons/{personId}/dogs
they are in fact specifying a specialized query language for their application. The same query could be
expressed as:
https://dogtracker.com/search?type=Dog&ownerId={personId}
Unlike Google, which provides a universal search language across all websites, URI templates define a
query language that is specific to a particular set of resources—your API. Designing a query language
specific to your API can be helpful since you can use knowledge of your data and use cases to define a
more usable and understandable query capability for your resources and to limit the cost of implementing
it. The downside is that the query language for each API has to be learned individually. If there were a
universal language for query that was supported by all APIs, the productivity of all API client application
developers would be improved. We will look at how to make query URLs regular and predictable in the
section entitled Designing query URLs.
Many people have been happy with the success of URI templates for locating resources via queries and
have ignored the value of the other mechanism—links. On the other extreme, some link advocates have
insisted on a single root model without the use of query/search. These two techniques are valuable
complements to each other for web APIs just as they are for the HTML web.
19
Including links, step 2
If all you do is add URL-valued properties like those shown above, you will already have made a significant
improvement to your API. You could make the URL-valued properties read-only, and continue to do
updates the way you do today. For example, if you want to change Lassie’s owner to the Duke of Rudling,
you would modify Lassie using a PUT or PATCH request to change the value of the ownerID to be the
Duke’s ID, and the ownerLink property would be recalculated by the server. If you are evolving an
existing API, this is a very practical step to take. However, if you are designing a new API, you may want to
go one step further with links.
To motivate the next step, consider the following example. Suppose dogs can be owned by either people
or institutions (e.g. companies, churches, governments, charities, and NGOs). Our dog tracker site is
motivated to allow this model so that they can track ownership of police dogs and other working dogs that
do not belong to individuals. Suppose also that the institutions are stored in a different table (or database)
from dogs, with their own set of IDs. At this point, it is no longer enough to have an ownerID property
with a simple value to reference the owner—you also need to specify what type of owner it is, so the
server knows what table (or database) to look in. Multiple solutions are possible: You could have ownerID
and ownerType properties, or you could have separate personOwnerID and institutionalOwnerID
properties only one of which may be set at a time. You could also invent a compound owner value that
encoded both the type and the ID. Each of these solutions may have its merits and demerits, but a very
elegant and flexible option is to use links to solve the problem. Imagine that we modify the example to
look like this:
{ “self”: “https://dogtracker.com/dogs/12345678”,
“id”: “12345678”,
“kind”: “Dog”
“name”: “Lassie”,
“furColor”: “brown”,
“owner”: “https://dogtracker.com/persons/98765432”
The explicit change is that ownerID property has been dropped, and there is a single owner field that is
URL-valued. An implicit change is that the URL-valued owner property is now read-write rather than just
read-only. You can now easily accommodate both human and institutional owners by simply setting the
property to be the URL of a person or the URL of an institution. Clients have to be aware that the owner
URL may point to different sorts of resources, and they will have to be prepared to react differently
according to the data returned when navigating this link. Note that this is exactly the behavior of a web
browser—the browser will follow a link in a web page and then act appropriately according to what it finds
20