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

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. [ LESS HYPE, MORE MEDIA, PLEASE ]
    HYPERMEDIA APIs

    View Slide

  2. WYNNNETHERLAND

    View Slide

  3. @pengwynn

    View Slide

  4. @pengwynn

    View Slide

  5. WYNN.FM

    View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. View Slide

  12. View Slide

  13. I write API wrappers.

    View Slide

  14. View Slide

  15. Wynn Netherland
    Flightless waterfowl

    View Slide

  16. Wynn Netherland
    Defacto API Evangelist

    View Slide

  17. View Slide

  18. Our industry loves buzzwords.

    View Slide

  19. “Nice site. Is it RESPONSIVE?”

    View Slide

  20. “Nice database. Is it SCHEMALESS?”

    View Slide

  21. “Nice API. Is it HYPERMEDIA-DRIVEN?”

    View Slide

  22. Nice questions. HACKER NEWS much?

    View Slide

  23. WHAT THE HECK IS HYPERMEDIA?

    View Slide

  24. >

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  33. View Slide

  34. View Slide

  35. View Slide

  36. HATEOAS

    View Slide

  37. HATEOAS
    Rhymes with Cheerios

    View Slide

  38. HATEOAS
    Rhymes with Cheerios

    View Slide

  39. HATEOAS

    View Slide

  40. HATEOAS
    A REST client enters a REST application through a simple fixed URL.
    *

    View Slide

  41. 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.
    *

    View Slide

  42. 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.
    *

    View Slide

  43. A SIMPLE EXAMPLE

    View Slide

  44. $ curl https://status.github.com/api.json

    View Slide

  45. $ curl https://status.github.com/api.json
    API root, our simple, fixed URL

    View Slide

  46. $ curl https://status.github.com/api.json
    {"status_url":"https://status.github.com/api/
    status.json","messages_url":"https://status.github.com/
    api/messages.json","last_message_url":"https://
    status.github.com/api/last-message.json"}
    Resource representation

    View Slide

  47. $ curl https://status.github.com/api.json
    {"status_url":"https://status.github.com/api/
    status.json","messages_url":"https://status.github.com/
    api/messages.json","last_message_url":"https://
    status.github.com/api/last-message.json"}
    Link relation

    View Slide

  48. $ curl https://status.github.com/api.json \
    | jq -r '.status_url'
    https://status.github.com/api/status.json

    View Slide

  49. $ curl https://status.github.com/api.json \
    | jq -r '.status_url'
    https://status.github.com/api/status.json

    View Slide

  50. $ curl https://status.github.com/api.json \
    | jq -r '.status_url' | xargs curl
    Follow link relation

    View Slide

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

    View Slide

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

    View Slide

  53. $ curl https://status.github.com/api.json \
    | jq -r '.status_url' | xargs curl \
    | jq -r '.status'

    View Slide

  54. DETOUR

    View Slide

  55. brew install jq

    View Slide

  56. THE REAL J-QUERY™

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  60. HYPERMEDIA TYPES

    View Slide

  61. 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.
    *

    View Slide

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

    View Slide

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

    View Slide

  64. Neither is JSON.

    View Slide

  65. BUT

    View Slide

  66. BUT

    View Slide

  67. “Hypermedia Types are MIME media types...”

    View Slide

  68. “Hypermedia Types are MIME media types...”
    “...because a MIME is a terrible thing to waste.”
    amirite?

    View Slide

  69. HAL

    View Slide

  70. View Slide

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

    View Slide

  72. 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.*

    View Slide

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

    View Slide

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

    View Slide

  75. COLLECTION+JSON

    View Slide

  76. COLLECTION+JSON is a JSON-based read/write hypermedia-type designed
    to support management and querying of simple collections.

    View Slide

  77. // 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.

    View Slide

  78. // 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.

    View Slide

  79. // 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.

    View Slide

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

    View Slide

  81. DETOUR

    View Slide

  82. “I think I'll just `curl` up with a nice red
    and read some specs.”
    - Nobody Evar

    View Slide

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

    View Slide

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

    View Slide

  85. Specs are a pain to write.

    View Slide

  86. Specs are a pain to read.

    View Slide

  87. If you've ever used an OAuth library,
    hug the developer.

    View Slide

  88. URI TEMPLATES

    View Slide

  89. 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}",
    ...

    View Slide

  90. 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}",
    ...

    View Slide

  91. # 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"

    View Slide

  92. repository_search_url: "/legacy/repos/search/{keyword}{?language,start_page}"

    View Slide

  93. HYPERMEDIA AGENTS

    View Slide

  94. SAWYER

    View Slide

  95. SAWYER

    View Slide

  96. endpoint = "http://localhost:9393/"
    agent = Sawyer::Agent.new(endpoint)
    root = agent.start
    root.data.rels[:users].get.data

    View Slide

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

    View Slide

  98. FARADAY

    View Slide

  99. View Slide

  100. GARTNER HYPE CYCLE

    View Slide

  101. Technology Trigger
    GARTNER HYPE CYCLE

    View Slide

  102. Technology Trigger
    Peak of Inflated Expectations
    GARTNER HYPE CYCLE

    View Slide

  103. Technology Trigger
    Peak of Inflated Expectations
    Trough of Disillusionment
    GARTNER HYPE CYCLE

    View Slide

  104. Technology Trigger
    Peak of Inflated Expectations
    Trough of Disillusionment
    Slope of Enlightenment
    GARTNER HYPE CYCLE

    View Slide

  105. Technology Trigger
    Peak of Inflated Expectations
    Trough of Disillusionment
    Slope of Enlightenment
    Plateau of Productivity
    GARTNER HYPE CYCLE

    View Slide

  106. API #REALTALK

    View Slide

  107. YOUR API IS HYPOMEDIA

    View Slide

  108. DEMO

    View Slide

  109. DEVELOPERS DON'T READ YOUR DOCS

    View Slide

  110. YOU'RE NOT DOGFOODING IT

    View Slide

  111. BUILD SOMETHING MEANINGFUL WITH YOUR API.

    View Slide

  112. Janky
    BUILD SOMETHING MEANINGFUL WITH YOUR API.

    View Slide

  113. Janky
    Heaven
    BUILD SOMETHING MEANINGFUL WITH YOUR API.

    View Slide

  114. Janky
    Heaven
    Monitors
    BUILD SOMETHING MEANINGFUL WITH YOUR API.

    View Slide

  115. Janky
    Team
    Heaven
    Monitors
    BUILD SOMETHING MEANINGFUL WITH YOUR API.

    View Slide

  116. Janky
    Team
    Hire
    Heaven
    Monitors
    BUILD SOMETHING MEANINGFUL WITH YOUR API.

    View Slide

  117. Janky
    Team
    Hire
    Heaven
    Monitors
    The Setup™
    BUILD SOMETHING MEANINGFUL WITH YOUR API.

    View Slide

  118. Janky
    Team
    Hire
    Heaven
    Monitors
    The Setup™
    Graph Store
    BUILD SOMETHING MEANINGFUL WITH YOUR API.

    View Slide

  119. How GitHub uses the GitHub API.

    View Slide

  120. AuthN
    How GitHub uses the GitHub API.

    View Slide

  121. AuthN
    AuthZ
    How GitHub uses the GitHub API.

    View Slide

  122. AuthN
    AuthZ
    Merging
    How GitHub uses the GitHub API.

    View Slide

  123. AuthN
    AuthZ
    Merging
    Commit Status
    How GitHub uses the GitHub API.

    View Slide

  124. AuthN
    AuthZ
    Merging
    Commit Status
    GFM
    How GitHub uses the GitHub API.

    View Slide

  125. Do you GET me?

    View Slide

  126. GET /me?
    HTTP/1.1 200 OK
    Server: example.com
    Content-Type: application/json; charset=utf-8
    Connection: keep-alive
    Status: 200 OK

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  132. GET /me?
    HTTP/1.1 403 FORBIDDEN
    Server: example.com
    Content-Type: application/json; charset=utf-8
    Connection: keep-alive
    Status: 403 FORBIDDEN

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  136. /302 me

    View Slide

  137. /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.

    View Slide

  138. ETAGS ARE COOL. NOBODY USES 'EM.

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  145. $ 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

    View Slide

  146. N+1 OVER HTTP IS EXPENSIVE, YO

    View Slide

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

    View Slide

  148. Thanks.

    View Slide