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

Designing Hypermedia APIs

Designing Hypermedia APIs

This is the version of "Designing Hypermedia APIs" I gave at Øredev 2012.

Steve Klabnik

November 08, 2012
Tweet

More Decks by Steve Klabnik

Other Decks in Programming

Transcript

  1. “People are fairly good at short-term design, and usually awful

    at long-term design. Most don’t think they need to design past the current release.” Problem Sunday, November 18, 12
  2. “But what distinguishes the worst architect from the best of

    bees is this, that the architect raises his structure in imagination before he erects it in reality.” Karl Marx Sunday, November 18, 12
  3. GET / HTTP/1.1 Host: www.example.com HTTP/1.1 200 OK Content-Length: 438

    Content-Type: application/json Link: </profile>; rel="profile" { "statuses":[ { "body":"neato", "username":"steveklabnik" }, { "body":"testing testing", "username":"steveklabnik" } ], "template":{ "body":"", "username":"" }, "links":[ { "rel":"collection", "href":"/" } ] } Sunday, November 18, 12
  4. GET / HTTP/1.1 Host: www.example.com HTTP/1.1 200 OK Content-Length: 438

    Content-Type: application/json Link: </profile>; rel="profile" { "statuses":[ { "body":"neato", "username":"steveklabnik" }, { "body":"testing testing", "username":"steveklabnik" } ], "template":{ "body":"", "username":"" }, "links":[ { "rel":"collection", "href":"/" } ] } Sunday, November 18, 12
  5. GET / HTTP/1.1 Host: www.example.com HTTP/1.1 200 OK Content-Length: 438

    Content-Type: application/json Link: </profile>; rel="profile" { "statuses":[ { "body":"neato", "username":"steveklabnik" }, { "body":"testing testing", "username":"steveklabnik" } ], "template":{ "body":"", "username":"" }, "links":[ { "rel":"collection", "href":"/statuses" } ] } Sunday, November 18, 12
  6. POST / HTTP/1.1 Host: www.example.com { "body":"hello, hypermedia", "username":"steveklabnik" }

    HTTP/1.1 201 Created Content-Length: 438 Content-Type: application/json Link: </profile>; rel="profile" Location: "/" {"message":"you are being redirected"} Sunday, November 18, 12
  7. GET / HTTP/1.1 Host: www.example.com HTTP/1.1 200 OK Content-Length: 438

    Content-Type: application/json Link: </profile>; rel="profile" { "statuses":[ { "body":"neato", "username":"steveklabnik" }, { "body":"testing testing", "username":"steveklabnik" }, { "body":"hello, hypermedia", "username":"steveklabnik" }, ], "template":{ "body":"", "username":"" }, "links":[ { "rel":"collection", "href":"/" } ]} Sunday, November 18, 12
  8. Coupling - Demeter class Foo def initialize(bar) @bar = bar

    end def process @bar.qux.fetch_data end end Sunday, November 18, 12
  9. Coupling - Demeter class Foo def initialize(bar) @bar = bar

    end def process @bar.qux.fetch_data end end Sunday, November 18, 12
  10. Coupling - Demeter describe Foo do it “fetches data” do

    bar = Bar.new bar.stub(:qux => stub(:fetch_data => “data”) ) expect(Foo.new(bar).process).to eq(“data”) end end Sunday, November 18, 12
  11. Coupling - Demeter describe Foo do it “fetches data” do

    bar = Bar.new bar.stub(:qux => stub(:fetch_data => “data”) ) expect(Foo.new(bar).process).to eq(“data”) end end Sunday, November 18, 12
  12. Coupling - Demeter You can tell something is coupled because

    it breaks when you change it. Sunday, November 18, 12
  13. Decoupling GET / HTTP/1.1 Host: www.example.com Accept: application/json HTTP/1.1 200

    OK Content-Length: 438 Content-Type: application/json Sunday, November 18, 12
  14. Decoupling GET / HTTP/1.1 Host: www.example.com Accept: application/json HTTP/1.1 200

    OK Content-Length: 438 Content-Type: application/json Sunday, November 18, 12
  15. Media Types Media type definition contains all information needed to

    implement a client and server for a given type. Sunday, November 18, 12
  16. GET / HTTP/1.1 Host: www.example.com HTTP/1.1 200 OK Content-Length: 438

    Content-Type: application/json Link: </profile>; rel="profile" { "statuses":[ { "body":"neato", "username":"steveklabnik" }, { "body":"testing testing", "username":"steveklabnik" }, { "body":"hello, hypermedia", "username":"steveklabnik" }, ], "template":{ "body":"", "username":"" }, "links":[ { "rel":"collection", "href":"/" } ]} Sunday, November 18, 12
  17. GET /profile HTTP/1.1 Host: www.example.com HTTP/1.1 200 OK Content-Length: 438

    Content-Type: text/plain This server emits "microblogging JSON." ## Keys You can expect the following keys: statuses, template, links ## Link Relations You can expect a "collection" relation. GETing the URI will fetch the list of all statuses. POSTing a template to this URI will create a new status. Sunday, November 18, 12
  18. Media Types def reply_to_tweet tweets = get_list_of_tweets reply_to = display_to_user(tweets)

    reply_data = display_require_form send_reply(reply_to, reply_data) end Sunday, November 18, 12
  19. Media Types def reply_to_tweet tweets = get_list_of_tweets reply_to = display_to_user(tweets)

    reply_data = display_require_form send_reply(reply_to, reply_data) end Sunday, November 18, 12
  20. Media Types Hypermedia Affordance <a href=”...” rel=”foo”>foo</a> <form action=”” method=”GET”>

    <input type=”text” name=”q” /> <input type=”submit” /> </form> Sunday, November 18, 12
  21. Deleuze’s Materialism Intensive Property: does not depend on the amount

    of substance (density) Extensive Property: does depend on amount of substance (mass) Sunday, November 18, 12
  22. M -> C -> M’ C -> M -> C’

    Sunday, November 18, 12
  23. Pagination The pagination info is included in the Link header.

    It is important to follow these Link header values instead of constructing your own URLs. In some instances, such as in the Commits API, pagination is based on SHA1 and not on page number. Link: <https://api.github.com/user/repos? page=3&per_page=100>; rel="next", <https://api.github.com/user/repos? page=50&per_page=100>; rel="last" Sunday, November 18, 12
  24. Hypermedia Proxy Pattern {"links": [ {"rel": "self", "href": "http://localhost:9292/status/ 2"}],

    "body":"hello, world", "location":"Los Angeles" } Sunday, November 18, 12