Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Hypermedia APIs - less hype more media, please

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
Tweet

More Decks by Wynn Netherland

Other Decks in Programming

Transcript

  1. >

  2. 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 >
  3. 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 >
  4. 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.
  5. 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.
  6. 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.
  7. 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.
  8. 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.
  9. 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.
  10. 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. *
  11. 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. *
  12. $ curl https://status.github.com/api.json \ | jq -r '.status_url' | xargs

    curl {"status":"good","last_updated":"2013-02-27T21:31:55Z"}
  13. $ curl https://status.github.com/api.json \ | jq -r '.status_url' | xargs

    curl {"status":"good","last_updated":"2013-02-27T21:31:55Z"}
  14. 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
  15. 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
  16. 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
  17. 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. *
  18. “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
  19. “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
  20. HAL

  21. 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
  22. 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.*
  23. 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.
  24. 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
  25. // 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.
  26. // 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.
  27. // 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.
  28. 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
  29. “I think I'll just `curl` up with a nice red

    and read some specs.” - Nobody Evar
  30. 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
  31. 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.
  32. 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}", ...
  33. 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}", ...
  34. # 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"
  35. 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
  36. Technology Trigger Peak of Inflated Expectations Trough of Disillusionment Slope

    of Enlightenment Plateau of Productivity GARTNER HYPE CYCLE
  37. 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
  38. 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
  39. 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
  40. 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
  41. 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
  42. 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
  43. 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
  44. 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
  45. /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.
  46. 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
  47. 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
  48. 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
  49. 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
  50. 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
  51. 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
  52. $ 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
  53. 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