Share
Embed
Hypermedia APIs - less hype more media, please
Hypermedia-driven APIs put more burden on client developers. Sometimes that's worth it.
Wynn Netherland
March 02, 2013
Transcript
148 slides
-
hubot wiki me hypermedia Hypermedia is used as a logical
extension of the term hypertext in which graphics, audio, video, plain text and hyperlinks intertwine to create a generally non-linear medium of information. This contrasts with the broader term multimedia, which may be used to describe non-interactive linear presentations as well as hypermedia. It is also related to the field of electronic literature. The term was first used in a 1965 article by Ted Nelson. https://en.wikipedia.org/wiki/Hypermedia > -
hubot wiki me hypermedia Hypermedia is used as a logical
extension of the term hypertext in which graphics, audio, video, plain text and hyperlinks intertwine to create a generally non-linear medium of information. This contrasts with the broader term multimedia, which may be used to describe non-interactive linear presentations as well as hypermedia. It is also related to the field of electronic literature. The term was first used in a 1965 article by Ted Nelson. https://en.wikipedia.org/wiki/Hypermedia > -
hypermedia Hypermedia is used as a logical extension of the
term hypertext in which graphics, audio, video, plain text and hyperlinks intertwine to create a generally non-linear medium of information. -
hypermedia Hypermedia is used as a logical extension of the
term hypertext in which graphics, audio, video, plain text and hyperlinks intertwine to create a generally non-linear medium of information. -
hypertext Hypertext is text displayed on a computer display or
other electronic device with references (hyperlinks) to other text that the reader can immediately access, usually by a mouse click, keypress sequence or by touching the screen. -
hypertext Hypertext is text displayed on a computer display or
other electronic device with references (hyperlinks) to other text that the reader can immediately access, usually by a mouse click, keypress sequence or by touching the screen. -
hyperlink In computing, a hyperlink (or link) is a reference
to data that the reader can directly follow, or that is followed automatically. A hyperlink points to a whole document or to a specific element within a document. -
hyperlink In computing, a hyperlink (or link) is a reference
to data that the reader can directly follow, or that is followed automatically. A hyperlink points to a whole document or to a specific element within a document. -
HATEOAS A REST client enters a REST application through a
simple fixed URL. * -
HATEOAS A REST client enters a REST application through a
simple fixed URL. * All future actions the client may take are discovered within resource representations returned from the server. * -
HATEOAS A REST client enters a REST application through a
simple fixed URL. * All future actions the client may take are discovered within resource representations returned from the server. * The media types used for these representations, and the link relations they may contain, are standardized. * -
$ curl https://status.github.com/api.json \ | jq -r '.status_url' | xargs
curl Follow link relation -
$ curl https://status.github.com/api.json \ | jq -r '.status_url' | xargs
curl {"status":"good","last_updated":"2013-02-27T21:31:55Z"} -
$ curl https://status.github.com/api.json \ | jq -r '.status_url' | xargs
curl {"status":"good","last_updated":"2013-02-27T21:31:55Z"} -
$ curl https://status.github.com/api.json \ | jq -r '.status_url' | xargs
curl \ | jq -r '.status' -
curl https://api.github.com/repos/pengwynn/octokit/commits \ | jq -r '.[].author.url' | uniq -u
https://api.github.com/users/patcon https://api.github.com/users/pengwynn https://api.github.com/users/sferik https://api.github.com/users/joeyw https://api.github.com/users/camelmasa https://api.github.com/users/pengwynn -
curl https://api.github.com/repos/pengwynn/octokit/commits \ | jq -r '.[].author.url' | uniq -u
https://api.github.com/users/patcon https://api.github.com/users/pengwynn https://api.github.com/users/sferik https://api.github.com/users/joeyw https://api.github.com/users/camelmasa https://api.github.com/users/pengwynn -
curl https://api.github.com/repos/pengwynn/octokit/commits \ | jq -r '.[].author.url' | uniq -u
https://api.github.com/users/patcon https://api.github.com/users/pengwynn https://api.github.com/users/sferik https://api.github.com/users/joeyw https://api.github.com/users/camelmasa https://api.github.com/users/pengwynn -
HATEOAS A REST client enters a REST application through a
simple fixed URL. * All future actions the client may take are discovered within resource representations returned from the server. * The media types used for these representations, and the link relations they may contain, are standardized. * -
“Hypermedia Types are MIME media types that contain native hyper-linking
semantics that induce application flow. For example, HTML is a hypermedia type; XML is not.” Mike Amundsen -
“Hypermedia Types are MIME media types that contain native hyper-linking
semantics that induce application flow. For example, HTML is a hypermedia type; XML is not.” Mike Amundsen -
“Hypermedia Types are MIME media types...” “...because a MIME is
a terrible thing to waste.” amirite? -
HAL provides simple linking in data { "_links": { "self":
{ "href": "/orders" }, "next": { "href": "/orders?page=2" }, "find": { "href": "/orders{?id}", "templated": true }, "admin": [ { "href": "/admins/2", "title": "Fred" }, { "href": "/admins/5", "title": "Kate" } ] }, currentlyProcessing: 14, shippedToday: 20, "_embedded": { "orders": [{ "_links": { "self": { "href": "/orders/123" }, ... application/hal+json -
HAL provides simple linking in data { "_links": { "self":
{ "href": "/orders" }, "next": { "href": "/orders?page=2" }, "find": { "href": "/orders{?id}", "templated": true }, "admin": [ { "href": "/admins/2", "title": "Fred" }, { "href": "/admins/5", "title": "Kate" } ] }, currentlyProcessing: 14, shippedToday: 20, "_embedded": { "orders": [{ "_links": { "self": { "href": "/orders/123" }, ... application/hal+json in two flavors.* -
HAL provides simple linking in data { "_links": { "self":
{ "href": "/orders" }, "next": { "href": "/orders?page=2" }, "find": { "href": "/orders{?id}", "templated": true }, "admin": [ { "href": "/admins/2", "title": "Fred" }, { "href": "/admins/5", "title": "Kate" } ] }, currentlyProcessing: 14, shippedToday: 20, "_embedded": { "orders": [{ "_links": { "self": { "href": "/orders/123" }, ... application/hal+json in two flavors.* * but one is XML and has 52% more fiber. -
HAL provides simple linking in data { "_links": { "self":
{ "href": "/orders" }, "next": { "href": "/orders?page=2" }, "find": { "href": "/orders{?id}", "templated": true }, "admin": [ { "href": "/admins/2", "title": "Fred" }, { "href": "/admins/5", "title": "Kate" } ] }, currentlyProcessing: 14, shippedToday: 20, "_embedded": { "orders": [{ "_links": { "self": { "href": "/orders/123" }, ... application/hal+json -
COLLECTION+JSON is a JSON-based read/write hypermedia-type designed to support management
and querying of simple collections. -
// sample collection object{ "collection" : { "version" : "1.0",
"href" : URI, "links" : [ARRAY], "items" : [ARRAY], "queries" : [ARRAY], "template" : {OBJECT}, "error" : {OBJECT} } } COLLECTION+JSON is a JSON-based read/write hypermedia-type designed to support management and querying of simple collections. -
// sample collection object{ "collection" : { "version" : "1.0",
"href" : URI, "links" : [ARRAY], "items" : [ARRAY], "queries" : [ARRAY], "template" : {OBJECT}, "error" : {OBJECT} } } application/vnd.collection+json COLLECTION+JSON is a JSON-based read/write hypermedia-type designed to support management and querying of simple collections. -
// sample collection object{ "collection" : { "version" : "1.0",
"href" : URI, "links" : [ARRAY], "items" : [ARRAY], "queries" : [ARRAY], "template" : {OBJECT}, "error" : {OBJECT} } } application/vnd.collection+json COLLECTION+JSON is a JSON-based read/write hypermedia-type designed to support management and querying of simple collections. Read up on the format in the draft spec. -
Why I'm down on Hypermedia Containers ...one of my main
principles in adopting hypermedia is to avoid educating developers on hypermedia as much as possible. I’m in the game of providing a useful API, not a system that shows off the possibilities of hypermedia and how deeply committed I am to its theories. ADAM KEYS @therealadam -
“I think I'll just `curl` up with a nice red
and read some specs.” - Nobody Evar -
What the SPEC? Hammer, et al. Expires November 2, 2012
[Page 10] Internet-Draft OAuth 2.0 May 2012 +--------+ +---------------+ | |--(A)------- Authorization Grant --------->| | | | | | | |<-(B)----------- Access Token -------------| | | | & Refresh Token | | | | | | | | +----------+ | | | |--(C)---- Access Token ---->| | | | | | | | | | | |<-(D)- Protected Resource --| Resource | | Authorization | | Client | | Server | | Server | | |--(E)---- Access Token ---->| | | | | | | | | | | |<-(F)- Invalid Token Error -| | | | | | +----------+ | | | | | | | |--(G)----------- Refresh Token ----------->| | | | | | | |<-(H)----------- Access Token -------------| | +--------+ & Optional Refresh Token +---------------+ Figure 2: Refreshing an Expired Access Token -
What the SPEC? Hammer, et al. Expires November 2, 2012
[Page 10] Internet-Draft OAuth 2.0 May 2012 +--------+ +---------------+ | |--(A)------- Authorization Grant --------->| | | | | | | |<-(B)----------- Access Token -------------| | | | & Refresh Token | | | | | | | | +----------+ | | | |--(C)---- Access Token ---->| | | | | | | | | | | |<-(D)- Protected Resource --| Resource | | Authorization | | Client | | Server | | Server | | |--(E)---- Access Token ---->| | | | | | | | | | | |<-(F)- Invalid Token Error -| | | | | | +----------+ | | | | | | | |--(G)----------- Refresh Token ----------->| | | | | | | |<-(H)----------- Access Token -------------| | +--------+ & Optional Refresh Token +---------------+ Figure 2: Refreshing an Expired Access Token Page breaks. Online. In 2013. -
curl https://api.github.com/ { current_user_url: "/user", authorizations_url: "/authorizations", emails_url: "/user/emails", emojis_url:
"/emojis", events_url: "/events", following_url: "/user/following{/target}", gists_url: "/gists{/gist_id}", hub_url: "/hub", issue_search_url: "/legacy/issues/search/{owner}/{repo}/{state}/{keyword}", issues_url: "/issues", keys_url: "/user/keys", notifications_url: "/notifications", organization_repositories_url: "/orgs/{org}/repos/{?type,page,per_page,sort}", organization_url: "/orgs/{org}", public_gists_url: "/gists/public", rate_limit_url: "/rate_limit", repository_url: "/repos/{owner}/{repo}", repository_search_url: "/legacy/repos/search/{keyword}{?language,start_page}", current_user_repositories_url: "/user/repos{?type,page,per_page,sort}", ... -
curl https://api.github.com/ { current_user_url: "/user", authorizations_url: "/authorizations", emails_url: "/user/emails", emojis_url:
"/emojis", events_url: "/events", following_url: "/user/following{/target}", gists_url: "/gists{/gist_id}", hub_url: "/hub", issue_search_url: "/legacy/issues/search/{owner}/{repo}/{state}/{keyword}", issues_url: "/issues", keys_url: "/user/keys", notifications_url: "/notifications", organization_repositories_url: "/orgs/{org}/repos/{?type,page,per_page,sort}", organization_url: "/orgs/{org}", public_gists_url: "/gists/public", rate_limit_url: "/rate_limit", repository_url: "/repos/{owner}/{repo}", repository_search_url: "/legacy/repos/search/{keyword}{?language,start_page}", current_user_repositories_url: "/user/repos{?type,page,per_page,sort}", ... -
# create the template tpl = URITemplate.new('https://api.github.com/repos/{owner}/{repo}) # Expand with
given placeholder values tpl.expand :owner => "pengwynn", :repo => "octokit" => "https://api.github.com/repos/pengwynn/octokit" -
module Octokit class Halogen LINK_REGEX = /_?url$/ def parse(data) links
= {} inline_links = data.keys.select {|k| k.to_s[LINK_REGEX] } inline_links.each do |key| rel_name = key.to_s == 'url' ? 'self' : key.to_s.gsub(LINK_REGEX, '') links[rel_name.to_sym] = data[key] end return data, links end end end -
Technology Trigger Peak of Inflated Expectations Trough of Disillusionment Slope
of Enlightenment GARTNER HYPE CYCLE -
Technology Trigger Peak of Inflated Expectations Trough of Disillusionment Slope
of Enlightenment Plateau of Productivity GARTNER HYPE CYCLE -
Janky Team Hire Heaven Monitors The Setup™ Graph Store BUILD
SOMETHING MEANINGFUL WITH YOUR API. -
GET /me? HTTP/1.1 200 OK Server: example.com Content-Type: application/json; charset=utf-8
Connection: keep-alive Status: 200 OK -
GET /me? HTTP/1.1 200 OK Server: example.com Content-Type: application/json; charset=utf-8
Connection: keep-alive Status: 200 OK Developer hears: :OK -
GET /me? HTTP/1.1 200 OK Server: example.com Content-Type: application/json; charset=utf-8
Connection: keep-alive Status: 200 OK Developer hears: :OK -
GET /me? HTTP/1.1 500 INTERNAL SERVER ERROR Server: example.com Content-Type:
application/json; charset=utf-8 Connection: keep-alive Status: 500 INTERNAL SERVER ERROR -
GET /me? HTTP/1.1 500 INTERNAL SERVER ERROR Server: example.com Content-Type:
application/json; charset=utf-8 Connection: keep-alive Status: 500 INTERNAL SERVER ERROR Developer hears: :DOH -
GET /me? HTTP/1.1 500 INTERNAL SERVER ERROR Server: example.com Content-Type:
application/json; charset=utf-8 Connection: keep-alive Status: 500 INTERNAL SERVER ERROR Developer hears: :DOH -
GET /me? HTTP/1.1 403 FORBIDDEN Server: example.com Content-Type: application/json; charset=utf-8
Connection: keep-alive Status: 403 FORBIDDEN -
GET /me? HTTP/1.1 403 FORBIDDEN Server: example.com Content-Type: application/json; charset=utf-8
Connection: keep-alive Status: 403 FORBIDDEN Developer hears: :NOPE -
GET /me? HTTP/1.1 302 FOUND Server: example.com Content-Type: application/json; charset=utf-8
Connection: keep-alive Status: 302 FOUND Location: https://example.com/over/there -
GET /me? HTTP/1.1 302 FOUND Server: example.com Content-Type: application/json; charset=utf-8
Connection: keep-alive Status: 302 FOUND Location: https://example.com/over/there Developer hears: :WAT -
/302 me The requested resource resides temporarily under a different
URI. Since the redirection might be altered on occasion, the client SHOULD continue to use the Request-URI for future requests. This response is only cacheable if indicated by a Cache- Control or Expires header field. -
curl -I https://api.github.com/users/defunkt HTTP/1.1 200 OK Server: nginx Date: Wed,
12 Sep 2012 14:07:43 GMT Content-Type: application/json; charset=utf-8 Connection: keep-alive Status: 200 OK Content-Length: 692 X-Content-Type-Options: nosniff X-RateLimit-Remaining: 4997 X-RateLimit-Limit: 5000 Cache-Control: public, s-maxage=60, max-age=60 Vary: Accept X-GitHub-Media-Type: github.beta ETag: "ef742caec0c19e2169ffb05e7d200d17" Last-Modified: Tue, 11 Sep 2012 02:52:21 GMT -
curl -I https://api.github.com/users/defunkt HTTP/1.1 200 OK Server: nginx Date: Wed,
12 Sep 2012 14:07:43 GMT Content-Type: application/json; charset=utf-8 Connection: keep-alive Status: 200 OK Content-Length: 692 X-Content-Type-Options: nosniff X-RateLimit-Remaining: 4997 X-RateLimit-Limit: 5000 Cache-Control: public, s-maxage=60, max-age=60 Vary: Accept X-GitHub-Media-Type: github.beta ETag: "ef742caec0c19e2169ffb05e7d200d17" Last-Modified: Tue, 11 Sep 2012 02:52:21 GMT -
curl -I https://api.github.com/users/defunkt HTTP/1.1 200 OK Server: nginx Date: Wed,
12 Sep 2012 14:07:43 GMT Content-Type: application/json; charset=utf-8 Connection: keep-alive Status: 200 OK Content-Length: 692 X-Content-Type-Options: nosniff X-RateLimit-Remaining: 4997 X-RateLimit-Limit: 5000 Cache-Control: public, s-maxage=60, max-age=60 Vary: Accept X-GitHub-Media-Type: github.beta ETag: "ef742caec0c19e2169ffb05e7d200d17" Last-Modified: Tue, 11 Sep 2012 02:52:21 GMT Cache policy -
curl -I https://api.github.com/users/defunkt HTTP/1.1 200 OK Server: nginx Date: Wed,
12 Sep 2012 14:07:43 GMT Content-Type: application/json; charset=utf-8 Connection: keep-alive Status: 200 OK Content-Length: 692 X-Content-Type-Options: nosniff X-RateLimit-Remaining: 4997 X-RateLimit-Limit: 5000 Cache-Control: public, s-maxage=60, max-age=60 Vary: Accept X-GitHub-Media-Type: github.beta ETag: "ef742caec0c19e2169ffb05e7d200d17" Last-Modified: Tue, 11 Sep 2012 02:52:21 GMT Fingerprint -
curl -I \ -H 'If-None-Match:"ef742caec0c19e2169ffb05e7d200d17" \ https://api.github.com/users/defunkt HTTP/1.1 304 Not
Modified Server: nginx Date: Wed, 12 Sep 2012 15:51:39 GMT Connection: keep-alive Status: 304 Not Modified X-RateLimit-Limit: 5000 X-Content-Type-Options: nosniff Vary: Accept ETag: "ef742caec0c19e2169ffb05e7d200d17" X-RateLimit-Remaining: 4997 Last-Modified: Wed, 12 Sep 2012 01:38:14 GMT Cache-Control: public, s-maxage=60, max-age=60 -
curl -H 'If-None-Match:"ef742caec0c19e2169ffb05e7d200d17" \ https://api.github.com/users/defunkt HTTP/1.1 304 Not Modified Server:
nginx Date: Wed, 12 Sep 2012 15:51:39 GMT Connection: keep-alive Status: 304 Not Modified X-RateLimit-Limit: 5000 X-Content-Type-Options: nosniff Vary: Accept ETag: "ef742caec0c19e2169ffb05e7d200d17" X-RateLimit-Remaining: 4997 Last-Modified: Wed, 12 Sep 2012 01:38:14 GMT Cache-Control: public, s-maxage=60, max-age=60 -
$ curl -i https://api.github.com/user HTTP/1.1 200 OK Cache-Control: private, max-age=60
ETag: "644b5b0155e6404a9cc4bd9d8b1ae730" Last-Modified: Thu, 05 Jul 2012 15:31:30 GMT Status: 200 OK Vary: Accept, Authorization, Cookie X-RateLimit-Limit: 5000 X-RateLimit-Remaining: 4996 $ curl -i https://api.github.com/user -H "If-Modified-Since: Thu, 05 Jul 2012 15:31:30 GMT" HTTP/1.1 304 Not Modified Cache-Control: private, max-age=60 Last-Modified: Thu, 05 Jul 2012 15:31:30 GMT Status: 304 Not Modified Vary: Accept, Authorization, Cookie X-RateLimit-Limit: 5000 X-RateLimit-Remaining: 4996 $ curl -i https://api.github.com/user -H 'If-None-Match: "644b5b0155e6404a9cc4bd9d8b1ae730"' HTTP/1.1 304 Not Modified Cache-Control: private, max-age=60 ETag: "644b5b0155e6404a9cc4bd9d8b1ae730" Last-Modified: Thu, 05 Jul 2012 15:31:30 GMT Status: 304 Not Modified -
Attribution Hand designed by Naomi Atkinson from The Noun Project
Cereal designed by Jacob Halton from The Noun Project Evil designed by Jim Lears from The Noun Project Console designed by Austin Andrews from The Noun Project Report designed by Doug Cavendish from The Noun Project Television designed by Piero Borgo from The Noun Project Person designed by Paulo Sá Ferreira from The Noun Project Detour designed by Dmitry Baranovskiy from The Noun Project Mime designed by Jonathan C. Dietrich from The Noun Project