$30 off During Our Annual Pro Sale. View Details »

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. Designing
    Hypermedia
    APIs
    @steveklabnik
    Sunday, November 18, 12

    View Slide

  2. Sunday, November 18, 12

    View Slide

  3. “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

    View Slide

  4. Solution?
    Sunday, November 18, 12

    View Slide

  5. Solution
    Flexibility
    Stability
    Sunday, November 18, 12

    View Slide

  6. “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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  10. 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: ; rel="profile"
    Location: "/"
    {"message":"you are being redirected"}
    Sunday, November 18, 12

    View Slide

  11. GET / HTTP/1.1
    Host: www.example.com
    HTTP/1.1 200 OK
    Content-Length: 438
    Content-Type: application/json
    Link: ; 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

    View Slide

  12. Adapters
    Sunday, November 18, 12

    View Slide

  13. Adapters
    Sunday, November 18, 12

    View Slide

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

    View Slide

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

    View Slide

  16. 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

    View Slide

  17. 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

    View Slide

  18. Coupling - Demeter
    Demeter is actually
    about types.
    @bar.qux.fetch_data
    Sunday, November 18, 12

    View Slide

  19. Coupling - Demeter
    You can tell something is
    coupled because it breaks
    when you change it.
    Sunday, November 18, 12

    View Slide

  20. Coupling - Demeter
    Stability: doesn’t break over time
    Flexibility: ability to change
    Sunday, November 18, 12

    View Slide

  21. Solution
    Radical
    decoupling
    Sunday, November 18, 12

    View Slide

  22. Sunday, November 18, 12

    View Slide

  23. Decoupling
    Sunday, November 18, 12

    View Slide

  24. Decoupling
    Sunday, November 18, 12

    View Slide

  25. 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

    View Slide

  26. 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

    View Slide

  27. Media Types
    Media type definition contains all
    information needed to implement a
    client and server for a given type.
    Sunday, November 18, 12

    View Slide

  28. GET / HTTP/1.1
    Host: www.example.com
    HTTP/1.1 200 OK
    Content-Length: 438
    Content-Type: application/json
    Link: ; 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

    View Slide

  29. 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

    View Slide

  30. Media Types
    Sunday, November 18, 12

    View Slide

  31. Media Types
    Clients
    Servers
    Media Type “microblog JSON”
    Sunday, November 18, 12

    View Slide

  32. “independent
    evolution”
    Media Types
    Sunday, November 18, 12

    View Slide

  33. Media Types
    https://dev.twitter.com/docs
    Sunday, November 18, 12

    View Slide

  34. Media Types
    RPC
    Sunday, November 18, 12

    View Slide

  35. 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

    View Slide

  36. 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

    View Slide

  37. Media Types
    Sunday, November 18, 12

    View Slide

  38. Media Types
    Sunday, November 18, 12

    View Slide

  39. Media Types




    Sunday, November 18, 12

    View Slide

  40. Media Types
    Hypermedia Affordance
    foo




    Sunday, November 18, 12

    View Slide

  41. Media Types
    Sunday, November 18, 12

    View Slide

  42. How to recognize process?
    Sunday, November 18, 12

    View Slide

  43. Sunday, November 18, 12

    View Slide

  44. 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

    View Slide

  45. Deleuze’s Materialism
    Intensive differences drive
    processes.
    Sunday, November 18, 12

    View Slide

  46. M -> C -> M’
    C -> M -> C’
    Sunday, November 18, 12

    View Slide

  47. {
    “post”:{
    “title”:”Foo”
    }
    }
    {
    “post”:{
    “title”:”Bar”
    }
    }
    Sunday, November 18, 12

    View Slide

  48. Deleuze’s Materialism
    Sunday, November 18, 12

    View Slide

  49. Media Types
    Collection
    +JSON,
    HAL, XHTML
    Sunday, November 18, 12

    View Slide

  50. 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: page=3&per_page=100>; rel="next",
    page=50&per_page=100>; rel="last"
    Sunday, November 18, 12

    View Slide

  51. ID -> URI
    "id": 2
    vs.
    {"rel": "self",
    "href": "http://localhost:9292/status/2"}
    Sunday, November 18, 12

    View Slide

  52. Hypermedia Proxy Pattern
    {"links": [
    {"rel": "self",
    "href": "http://localhost:9292/status/
    2"}],
    "body":"hello, world",
    "location":"Los Angeles"
    }
    Sunday, November 18, 12

    View Slide

  53. Thanks!
    @steveklabnik
    [email protected]
    http://designinghypermediaapis.com
    http://words.steveklabnik.com
    http://tutorials.jumpstartlab.com
    Sunday, November 18, 12

    View Slide