at the other end (HTML page, XML, PDF, JPEG, and more).
This is a powerful and flexible model that we
recommend, even though it places some requirements on clients that many are not used to.
A word of caution
We like the model of read-write URL-valued properties a lot, but it has consequences for server
implementers. In general, it is not a good idea to store full URLs with domain names in databases. If the
domain name of your system ever changes, all those URLs in the databases will be wrong. Sometimes it is
unavoidable to change the domain name of a production server, even though this is undesirable. Even if
you manage to avoid production server name changes, you want the freedom to use the same database
in different environments (like integration test, system test, and pre-production) with different domain
names, or just IP addresses. This means that the server should strip the scheme and authority from URLs
that identify its own resources before storing them, and put them back when they are requested. Any
URLs that identify resources that are completely external to your API will probably have to be stored as
absolute URLs. If you are not very experienced with absolute URLs in databases, don’t do it.
One way of avoiding the problem of absolute URLs in databases is to accept and produce relative URLs
in the API—especially those that start with a single /, called path-absolute URLs in the spec. This is a
reasonable approach, and it can avoid a number of implementation issues on the server. The trade-off
is that it pushes some burden onto the client because the client will have to turn those relative URLs
into absolute ones before it can use them. The good news for clients is that it is always possible to turn
a relative URL into an absolute URL using only general knowledge of URL standards—no knowledge
specific to your API is needed. The bad news is that in some cases it can be tricky for the client to establish
the base to which the URL is relative. If you are willing to learn how to produce and consume absolute
URLs on the server, you will create a better API for your users.
In general, writing servers that deal with URLs in resource representations is a skill that needs to be
learned. Adding readonly, relative URLs is fairly easy, and if that is the level of your ambition, you can
probably just go ahead and do it. Providing absolute URLs, and dealing with read-write URL-valued
properties both add some design and implementation problems for servers. The good news is that the
solutions to these problems are not very hard, but they are sufficiently subtle that it is easy to get them
wrong on the first try.
21
How should I represent links in my resources?
Our preferred approach for representing links is to use a simple JSON name/value pair, like this:
“owner”: “https://dogtracker.com/persons/98765432”
What are the advantages to this approach? It is very simple and consistent with the way we represent
simple properties in JSON. A disadvantage is that JSON has no encoding for URL values, so you have to
encode them as strings. If clients don’t have metadata, they will have to guess which strings are actually
URLs, perhaps by parsing all string values or relying on contextual information. This isn’t usually a
problem for custom-written client-side code, which generally has contextual information in the sense
that it was written by a programmer who had looked at the documentation and knew ahead of time which
properties are URL-valued. The problem arises when you’re writing general-purpose clients or library
code that can process a representation from any API without knowing its specifics.
There is a significant number of more complex patterns for representing links. We have included some
more information on these in the appendix. Whether you like or use any of these or not, we think it is
important to note that using links does not require anything more complex than simple JSON properties.
We have done a few projects using different styles and we currently favor keeping things as simple as
possible.
Who uses links?
The recommendations above for including links in APIs are not the most common practice in published
APIs, but they are used in some very prominent ones, such as the Google Drive API and GitHub. Below
are examples from the Google Drive API and GitHub. You might think that the problem domain of Google
Drive makes it especially suited to these techniques, but we believe that they apply equally to a broad
range of problem domains, and we have used them in many of our own designs.
22
Here’s the Google Drive API example:
GET
https://www.googleapis.com/drive/v2/files/0B8G-Akr_SmtmaEJneEZLYjB
HTTP/1.1 200 OK
kind”: “drive#file”,
“id”: “0B8G-Akr_SmtmaEJneEZLYjBBdWxxxxxxxxxxxxxxxx”,
“etag”: “\”btSRMRFBFi3NMGgScYWZpc9YNCI/MTQzNjU2NDk3OTU3Nw\””,
“selfLink”: “https://www.googleapis.com/drive/v2/files/0B8G-Akr_Smtm
“webContentLink”: “https://docs.google.com/uc?id=0B8G-Akr_SmtmaEJneE
“alternateLink”: “https://drive.google.com/file/d/0B8G-Akr_SmtmaEJne
“iconLink”: “https://ssl.gstatic.com/docs/doclist/images/icon_12_pdf
“thumbnailLink”: “https://lh4.googleusercontent.com/REcVMLRuNGsohM1C
“title”: “Soils Report.pdf”,
“mimeType”: “application/pdf”,
“parents”: [
“kind”: “drive#parentReference”,
“id”: “0AMG-Akr_SmtmUk9PVA”,
“selfLink”: “https://www.googleapis.com/drive/v2/files/0B8G-Akr_Sm
“parentLink”: “https://www.googleapis.com/drive/v2/files/0AMG-Akr_
“isRoot”: false
],
Note: The lines in this example are rather long and are truncated here on the right.
23
Here is a different example from the GitHub API:
“id”: 1,
“url”: “https://api.github.com/repos/octocat/Hello-World/issues/1347”,
“repository_url”: “https://api.github.com/repos/octocat/Hello-World”,
“Labels_url”:”https://api.github.com/repos/octocat/Hello-World/is sues/1347/labels{/name}”,
“comments_url”: “https://api.github.com/repos/octocat/Hello-World/issues/1347/comments”,
“events_url”: “https://api.github.com/repos/octocat/Hello-World/issues/1347/events”,
“html_url”: “https://github.com/octocat/Hello-World/issues/1347”,
“number”: 1347,
“state”: “open”,
“title”: “Found a bug”,
“body”: “I’m having a problem with this.”,
“user”: {
“login”: “octocat”,
“id”: 1,
“avatar_url”: “https://github.com/images/error/octocat_happy.gif”,
“gravatar_id”: “”,
“url”: “https://api.github.com/users/octocat”,
“html_url”: “https://github.com/octocat”,
“followers_url”: “https://api.github.com/users/octocat/followers”,
“following_url”: “https://api.github.com/users/octocat/following{/other_user}”,
“gists_url”: “https://api.github.com/users/octocat/gists{/gist_id}”,
“starred_url”: “https://api.github.com/users/octocat/starred{/owner}{/repo}”,
“subscriptions_url”: “https://api.github.com/users/octocat/subscriptions”,
“organizations_url”: “https://api.github.com/users/octocat/orgs”,
“repos_url”: “https://api.github.com/users/octocat/repos”,
“events_url”: “https://api.github.com/users/octocat/events{/privacy}”,
“received_events_url”: “https://api.github.com/users/octocat/received_events”,
“type”: “User”, “site_admin”: false
24
},
“labels”: [
“url”: “https://api.github.com/repos/octocat/Hello-World/labels/bug”,
“name”: “bug”,
“color”: “f29513”
],
You will notice that the GitHub values are not all simple URLs; some of them are URI Templates.
Templates allow the URLs of a whole family of resources to be communicated in one string, at the cost of
requiring the client to process the template.
More later
There is more to say about designing representations, and we will return to this topic in the section
entitled More on representation design. We now turn our attention to URL design.
25