The REST constraints tell us to design APIs according to HATEAOS (Hypertext as the Engine of Application State). The Richardson Maturity Model rates APIs according to the fulfillment of these constraints and assigns the highest rating (level 3) to the proper implementation of the HATEOAS ideas.
In short, HATEOAS APIs should return not only data to the caller but also metadata about how to interact with the data on a semantic level. The semantic level is important: Just giving information about CRUD operations is in general not sufficient (those are already defined by HTTP) – it should be interactions on a “business level”.
I got lots of questions about proper HATEOAS APIs at Level 3 according to the Richardson Maturity Model. So I went and studied some of the APIs I worked with recently.
Does the Spotify API follow HATEOAS?
The Spotify API comes to mind. It is a well-known company, with a quite well-known API. The API is a simple example since it allows only read access to most resources.
If you want to dive deeper into the topic, check out my recent video on the Spotify API.
The Spotify API links relevant resources via URLs and uses a couple of additional fields to describe the relation to the other resource. Let’s see what you get when you call the Spotify album API to get information about an album. We start with:
GET https://api.spotify.com/v1/albums/0sNOF9WDwhWunNAHPD3Baj
And get the following response:
{
"album_type" : "album",
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/2BTZIqw0ntH9MvilQ3ewNY"
},
"href" : "https://api.spotify.com/v1/artists/2BTZIqw0ntH9MvilQ3ewNY",
"id" : "2BTZIqw0ntH9MvilQ3ewNY",
"name" : "Cyndi Lauper",
"type" : "artist",
"uri" : "spotify:artist:2BTZIqw0ntH9MvilQ3ewNY"
} ],
…
"tracks" : {
"href" : "https://api.spotify.com/v1/albums/0sNOF9WDwhWunNAHPD3Baj/tracks?offset=0&limit=50",
"items" : [ {
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/2BTZIqw0ntH9MvilQ3ewNY"
},
"href" : "https://api.spotify.com/v1/artists/2BTZIqw0ntH9MvilQ3ewNY",
"id" : "2BTZIqw0ntH9MvilQ3ewNY",
"name" : "Cyndi Lauper",
"type" : "artist",
"uri" : "spotify:artist:2BTZIqw0ntH9MvilQ3ewNY"
} ],
"available_markets" : [ "AD", "AR", "AT", "AU", "BE", "BG", "BO", "BR", "CA", "CH", "CL", "CO", "CR", "CY", "CZ", "DE", "DK", "DO", "EC", "EE", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MC", "MT", "MX", "MY", "NI", "NL", "NO", "NZ", "PA", "PE", "PH", "PT", "PY", "RO", "SE", "SG", "SI", "SK", "SV", "TW", "UY" ],
"disc_number" : 1,
"duration_ms" : 305560,
"explicit" : false,
"external_urls" : {
"spotify" : "https://open.spotify.com/track/3f9zqUnrnIq0LANhmnaF0V"
},
"href" : "https://api.spotify.com/v1/tracks/3f9zqUnrnIq0LANhmnaF0V",
"id" : "3f9zqUnrnIq0LANhmnaF0V",
"name" : "Money Changes Everything",
"preview_url" : "https://p.scdn.co/mp3-preview/01bb2a6c9a89c05a4300aea427241b1719a26b06",
"track_number" : 1,
"type" : "track",
"uri" : "spotify:track:3f9zqUnrnIq0LANhmnaF0V"
}, {
…
} ],
"limit" : 50,
"next" : null,
"offset" : 0,
"previous" : null,
"total" : 13
},
"type" : "album",
"uri" : "spotify:album:0sNOF9WDwhWunNAHPD3Baj"
}
Checking for HATEOAS
HATEOAS is all about constructing the response of the API: Which information to put in and which information to link.
In the above album resource, you can see several links: Link to binary objects, such as images, links to streaming URLs, and also links to other APIs (https://api.spotify.com/v1 ...
). Those are the interesting ones: they link to several other resources.
The album resource contains links pointing to the Artist and the Tracks of the album. The link to the artists is the following construct:
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/2BTZIqw0ntH9MvilQ3ewNY"
},
"href" : "https://api.spotify.com/v1/artists/2BTZIqw0ntH9MvilQ3ewNY",
"id" : "2BTZIqw0ntH9MvilQ3ewNY",
"name" : "Cyndi Lauper",
"type" : "artist",
"uri" : "spotify:artist:2BTZIqw0ntH9MvilQ3ewNY"
} ],
Interesting to note is that the linked resource is referenced by a URL (https://api.spotify.com/v1/artists/2BTZIqw0ntH9MvilQ3ewNY
). Additional information describing the link is present, such as the ID of the linked resource, the name of the resource, the type of the linked resource, and an internal Spotify URI.
There’s no absolute standard as to how to represent hypermedia controls. Spotify has chosen one that works for them. Others may use ATOM links, or the Hypertext Application Language (HAL).
Is it really that simple?
Well, for reading API access. For writing API access (you need to POST data to create a new resource) it is more difficult. The API needs to communicate what operations are allowed and which parameters are allowed — solvable, but not as simple as the read-only case shown above.
With HATEOAS, the main challenge is the design of the client, it has a lot of responsibility, and needs to be built in a generic way since it has to interpret the dynamic metadata received from the server.
Why is this great API design?
The callers of the album API might want to display information about the artist of the album as well. With the REST/HATEOAS design, the callers already get a link that can be used to get that information, the callers don’t need to know how to construct the URL to get more information, it is already prepared. Just call it.
And it is not just the raw link. It is combined with valuable meta-information (type, name, id) that helps the callers to build dynamic applications.
It is an advantage, that the HATEOAS API can evolve smoothly: The API provider may change the response slightly and the client should not break, as long as the metadata for identifying the link relation remains stable. See more about HATEOAS APIs in the article about API versioning.
Another advantage is that it helps client developers explore the protocol, it is self-documenting.
Want to learn more? Check out the REST API Design Book.
Also published on Medium.