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

Launching GitHub's GraphQL API

Launching GitHub's GraphQL API

359024e7132672aaeef4a3e792be4ae5?s=128

Brooks Swinnerton

October 11, 2017
Tweet

Transcript

  1. GraphQL

  2. Hi, I’m Brooks

  3. I work at !

  4. Launching GitHub’s GraphQL API

  5. March 2008 API v1 April 2009 API v2 April 2011

    API v3 May 2017 API v4
  6. March 2008 API v1 April 2009 API v2 April 2011

    API v3 May 2017 API v4
  7. March 2008 API v1 April 2009 API v2 April 2011

    API v3 May 2017 API v4
  8. March 2008 API v1 April 2009 API v2 April 2011

    API v3 May 2017 API v4
  9. https://api.github.com/user GET

  10. !"""# verb https://api.github.com/user GET

  11. !"""""""""""""""""""""""""""# endpoint !"""# verb https://api.github.com/user GET

  12. { "login": "bswinnerton", "id": 934497, "avatar_url": "https://avatars1.githubusercontent.com/u/934497?v=4", "url": "https://api.github.com/users/bswinnerton", "html_url":

    "https://github.com/bswinnerton", "site_admin": true, "name": "Brooks Swinnerton", "location": "Brooklyn, NY", "email": "bswinnerton@gmail.com", "bio": ":octocat:", "public_repos": 32, "public_gists": 55, "followers": 231, "following": 60, "created_at": "2011-07-23T17:44:47Z", "updated_at": "2017-10-02T17:38:48Z", "private_gists": 172, "total_private_repos": 9, "owned_private_repos": 8, "disk_usage": 87918, "collaborators": 7, "plan": { "name": "developer", "space": 976562499, "collaborators": 0, "private_repos": 9999 } } https://api.github.com/user GET
  13. REST APIs return resources

  14. { "login": "bswinnerton", "id": 934497, "avatar_url": "https://avatars1.githubusercontent.com/u/934497?v=4", "url": "https://api.github.com/users/bswinnerton", "html_url":

    "https://github.com/bswinnerton", "site_admin": true, "name": "Brooks Swinnerton", "location": "Brooklyn, NY", "email": "bswinnerton@gmail.com", "bio": ":octocat:", "public_repos": 32, "public_gists": 55, "followers": 231, "following": 60, "created_at": "2011-07-23T17:44:47Z", "updated_at": "2017-10-02T17:38:48Z", "private_gists": 172, "total_private_repos": 9, "owned_private_repos": 8, "disk_usage": 87918, "collaborators": 7, "plan": { "name": "developer", "space": 976562499, "collaborators": 0, "private_repos": 9999 } } https://api.github.com/user GET
  15. { "login": "bswinnerton", "id": 934497, "name": "Brooks Swinnerton", "location": "Brooklyn,

    NY", "email": "bswinnerton@gmail.com", "bio": ":octocat:", ... } https://api.github.com/user GET
  16. https://api.github.com/user GET { "login": "bswinnerton", "id": 934497, "name": "Brooks Swinnerton",

    "location": "Brooklyn, NY", "email": "bswinnerton@gmail.com", "bio": ":octocat:", "public_repos": 32, "public_gists": 55, "private_gists": 172, "owned_private_repos": 8, ... }
  17. but how do we access those other resources?

  18. ✨ hypermedia ✨

  19. https://api.github.com/user GET { "login": "bswinnerton", "id": 934497, "name": "Brooks Swinnerton",

    "location": "Brooklyn, NY", "email": "bswinnerton@gmail.com", "bio": ":octocat:", "public_repos": 32, "public_gists": 55, "private_gists": 172, "owned_private_repos": 8, "url": "https://api.github.com/users/bswinnerton", "gists_url": "https://api.github.com/users/bswinnerton/gists{/ gist_id}", "repos_url": "https://api.github.com/users/bswinnerton/repos", ... }
  20. https://api.github.com/user GET { "login": "bswinnerton", "id": 934497, "name": "Brooks Swinnerton",

    "location": "Brooklyn, NY", "email": "bswinnerton@gmail.com", "bio": ":octocat:", "public_repos": 32, "public_gists": 55, "private_gists": 172, "owned_private_repos": 8, "url": "https://api.github.com/users/bswinnerton", "gists_url": "https://api.github.com/users/bswinnerton/gists{/ gist_id}", "repos_url": "https://api.github.com/users/bswinnerton/repos", ... }
  21. https://api.github.com/users/bswinnerton/repos GET

  22. https://api.github.com/users/bswinnerton/repos GET [ { "id": 82398282, "name": "launchbar-github", "private": false,

    "description": "A LaunchBar action for GitHub", "language": "JavaScript", "homepage": "http://launchbar-github.com", "owner": { "login": "bswinnerton", "id": 934497, "url": "https://api.github.com/users/bswinnerton", ... }, "url": "https://api.github.com/repos/bswinnerton/launchbar-github", "issues_url": "https://api.github.com/repos/bswinnerton/launchbar-github/ issues{/number}" }, ... ]
  23. https://api.github.com/users/bswinnerton/repos GET [ { "id": 82398282, "name": "launchbar-github", "private": false,

    "description": "A LaunchBar action for GitHub", "language": "JavaScript", "homepage": "http://launchbar-github.com", "owner": { "login": "bswinnerton", "id": 934497, "url": "https://api.github.com/users/bswinnerton", ... }, "url": "https://api.github.com/repos/bswinnerton/launchbar-github", "issues_url": "https://api.github.com/repos/bswinnerton/launchbar-github/ issues{/number}" }, ... ]
  24. https://api.github.com/repos/bswinnerton/launchbar-github/issues GET

  25. https://api.github.com/repos/bswinnerton/launchbar-github/issues GET [ { "id": 246489445, "number": 83, "title": "Error

    when viewing Gists of another user", "state": "open", "body": "I can't reproduce this in every case...", "user": { "login": "bswinnerton", "id": 934497, "url": "https://api.github.com/users/bswinnerton", ... }, "url": "https://api.github.com/repos/bswinnerton/launchbar-github/issues/ 83", "repository_url": "https://api.github.com/repos/bswinnerton/launchbar- github", ... }, ... ]
  26. API Server /user /repositories /issues

  27. API Server /user /repositories /issues

  28. "RESTful APIs are optimized for servers, not clients." - Mark

    Twain (I think)
  29. how can we put API consumers first?

  30. March 2008 API v1 April 2009 API v2 April 2011

    API v3 Early 2016 ?
  31. Enter GraphQL

  32. a data query language

  33. think SQL

  34. not Neo4j

  35. GraphQL is a specification

  36. { viewer { name email } }

  37. { viewer { name email } }

  38. { viewer { name email } }

  39. { viewer { name email } }

  40. { viewer { name email } } { "data": {

    "viewer": { "name": "Brooks Swinnerton", "email": "brooks@github.com" } } }
  41. https://api.github.com/graphql POST

  42. !"""# verb https://api.github.com/graphql POST

  43. !"""# verb !""""""""""""""""""""""""""""""# endpoint https://api.github.com/graphql POST

  44. https://api.github.com/graphql POST { user(login:"defunkt") { name bio } } {

    "data": { "user": { "name": "Chris Wanstrath", "bio": """ } } }
  45. features of the query language

  46. GraphQL is typed

  47. { viewer { name email } }

  48. { viewer { name email } } type RootQuery {

    viewer: User } type User { name: String email: String }
  49. { viewer { name email } } type RootQuery {

    viewer: User } type User { name: String email: String }
  50. { viewer { name email } } type RootQuery {

    viewer: User } type User { name: String email: String }
  51. { user(login:"defunkt") { name bio } }

  52. type RootQuery { user(login: String): User } type User {

    name: String bio: String } { user(login:"defunkt") { name bio } }
  53. { user(login:"defunkt") { name bio } } type RootQuery {

    user(login: String): User } type User { name: String bio: String }
  54. type RootQuery { user(login: String): User } type User {

    name: String bio: String } { user(login:"defunkt") { name bio } }
  55. type RootQuery { user(login: String): User } type User {

    name: String bio: String } { user(login:"defunkt") { name bio } }
  56. { licenses { name nickname url } }

  57. { licenses { name nickname url } }

  58. { licenses { name nickname url } } type RootQuery

    { licenses: [License]! } type License { name: String! nickname: String url: URL! }
  59. { licenses { name nickname url } } type RootQuery

    { licenses: [License]! } type License { name: String! nickname: String url: URL! }
  60. { licenses { name nickname url } } type RootQuery

    { licenses: [License]! } type License { name: String! nickname: String url: URL! }
  61. aliases

  62. { user(login: "defunkt") { name bio } user(login: "bswinnerton") {

    name bio } }
  63. { user(login: "defunkt") { name bio } user(login: "bswinnerton") {

    name bio } } { "data": { "user": { "name": "Chris Wanstrath", "bio": """ }, "user": { "name": "Brooks Swinnerton", "bio": "#$" } } }
  64. { user(login: "defunkt") { name bio } user(login: "bswinnerton") {

    name bio } } { "data": { "user": { "name": "Chris Wanstrath", "bio": """ }, "user": { "name": "Brooks Swinnerton", "bio": "#$" } } }
  65. { chris: user(login: "defunkt") { name bio } brooks: user(login:

    "bswinnerton") { name bio } }
  66. { "data": { "chris": { "name": "Chris Wanstrath", "bio": """

    }, "brooks": { "name": "Brooks Swinnerton", "bio": "#$" } } }
  67. { "data": { "chris": { "name": "Chris Wanstrath", "bio": """

    }, "brooks": { "name": "Brooks Swinnerton", "bio": "#$" } } }
  68. fragments

  69. { chris: user(login: "defunkt") { name bio } brooks: user(login:

    "bswinnerton") { name bio } }
  70. { chris: user(login: "defunkt") { ...UserInfo } brooks: user(login: "bswinnerton")

    { ...UserInfo } } fragment UserInfo on User { name bio }
  71. variables

  72. { user(login:"defunkt") { name bio } }

  73. query { user(login:"defunkt") { name bio } }

  74. query($login:String!) { user(login:$login) { name bio } }

  75. { "login": "defunkt" } query($login:String!) { user(login:$login) { name bio

    } }
  76. { "data": { "user": { "name": "Chris Wanstrath", "bio": """

    } } } { "login": "defunkt" } query($login:String!) { user(login:$login) { name bio } }
  77. mutations

  78. mutation { createProject(input:{ownerId:"1234",name:"to do"}) { project { url } }

    }
  79. mutation { createProject(input:{ownerId:"1234",name:"to do"}) { project { url } }

    }
  80. mutation { createProject(input:{ownerId:"1234",name:"to do"}) { project { url } }

    }
  81. mutation { createProject(input:{ownerId:"1234",name:"to do"}) { project { url } }

    }
  82. { "data": { "createProject": { "project": { "url": "https://github.com/rails/rails/projects/1" }

    } } }
  83. Relay

  84. { viewer { repositories { totalCount } } }

  85. { viewer { repositories { totalCount } } } {

    "data": { "viewer": { "repositories": { "totalCount": 65 } } } }
  86. { viewer { repositories(first:2) { edges { node { name

    } } } } }
  87. { viewer { repositories(first:2) { edges { node { name

    } } } } }
  88. viewer dotfiles failed startup code resume blog million dollar idea

    code
  89. User Repository Repository Repository Repository Repository Edges

  90. User Repository Repository Repository Repository Repository Nodes

  91. { viewer { repositories(first:2) { edges { node { name

    } } } } }
  92. { viewer { repositories(first:2) { edges { node { name

    } } } } }
  93. { viewer { repositories(first:2) { edges { node { name

    } } } } } { "data": { "viewer": { "repositories": { "edges": [ { "node": { "name": "nyc-restaurant-grades" } }, { "node": { "name": "launchbar-github" } } ] } } } }
  94. { viewer { repositories(first:2) { edges { cursor node {

    name } } } } } { "data": { "viewer": { "repositories": { "edges": [ { "cursor": "Y3Vyc29yOnYyOpHOA5rd9g==", "node": { "name": "nyc-restaurant-grades" } }, { "cursor": "Y3Vyc29yOnYyOpHOBOlMSg==", "node": { "name": "launchbar-github" } } ] } } } }
  95. { viewer { repositories(first:2) { edges { cursor node {

    name } } } } } { "data": { "viewer": { "repositories": { "edges": [ { "cursor": "Y3Vyc29yOnYyOpHOA5rd9g==", "node": { "name": "nyc-restaurant-grades" } }, { "cursor": "Y3Vyc29yOnYyOpHOBOlMSg==", "node": { "name": "launchbar-github" } } ] } } } }
  96. { viewer { repositories(first:2) { edges { cursor node {

    name } } } } } { "data": { "viewer": { "repositories": { "edges": [ { "cursor": "Y3Vyc29yOnYyOpHOA5rd9g==", "node": { "name": "nyc-restaurant-grades" } }, { "cursor": "Y3Vyc29yOnYyOpHOBOlMSg==", "node": { "name": "launchbar-github" } } ] } } } }
  97. { viewer { repositories(first:2, after:"Y3Vyc29yOnYyOpHOBOlMSg==") { edges { node {

    name } } } } }
  98. { "data": { "viewer": { "repositories": { "edges": [ {

    "cursor": "Y3Vyc29yOnYyOpHOAIibww==", "node": { "name": "bswinnerton.github.io" } }, { "cursor": "Y3Vyc29yOnYyOpHOArDTpw==", "node": { "name": "dotfiles" } } ] } } } }
  99. GraphQL is introspectable

  100. documentation and client generation, are free

  101. https://developer.github.com/v4/explorer/

  102. https://developer.github.com/v4/explorer/

  103. https://developer.github.com/v4/explorer/

  104. https://developer.github.com/v4/explorer/

  105. https://developer.github.com/v4/explorer/

  106. https://developer.github.com/v4/explorer/

  107. https://developer.github.com/v4/explorer/

  108. https://developer.github.com/v4/explorer/

  109. https://developer.github.com/v4/explorer/

  110. https://developer.github.com/v4/explorer/

  111. https://developer.github.com/v4/explorer/

  112. https://developer.github.com/v4/explorer/

  113. https://developer.github.com/v4/explorer/

  114. https://developer.github.com/v4/explorer/

  115. https://developer.github.com/v4/explorer/

  116. https://developer.github.com/v4/explorer/

  117. multiple resources in one round trip

  118. API Server /user /repositories /issues

  119. API Server /graphql

  120. how can we put API consumers first?

  121. March 2008 API v1 April 2009 API v2 April 2011

    API v3 Early 2016 ?
  122. March 20, 2016: Proposal submitted April 6, 2016: Proof of

    concept April 12, 2016: New team created September 4th, 2016: Early access May 22, 2017: API v4
  123. March 20, 2016: Proposal submitted April 6, 2016: Proof of

    concept April 12, 2016: New team created September 4th, 2016: Early access May 22, 2017: API v4
  124. March 20, 2016: Proposal submitted April 6, 2016: Proof of

    concept April 12, 2016: New team created September 4th, 2016: Early access May 22, 2017: API v4
  125. March 20, 2016: Proposal submitted April 6, 2016: Proof of

    concept April 12, 2016: New team created September 4th, 2016: Early access May 22, 2017: API v4
  126. March 20, 2016: Proposal submitted April 6, 2016: Proof of

    concept April 12, 2016: New team created September 4th, 2016: Early access May 22, 2017: API v4
  127. March 20, 2016: Proposal submitted April 6, 2016: Proof of

    concept April 12, 2016: New team created September 4th, 2016: Early access May 22, 2017: API v4 Today: 200 million queries/day
  128. Rate Limiting

  129. REST API: 5,000 req/hour

  130. GraphQL: ?

  131. GraphQL: 500,000 node count

  132. GraphQL: 5,000 point score

  133. { viewer { repositories(last:100) { edges { node { name

    issues(last:50) { edges { node { title } } } } } } } rateLimit(dryRun:true) { nodeCount } }
  134. { viewer { repositories(last:100) { edges { node { name

    issues(last:50) { edges { node { title } } } } } } } rateLimit(dryRun:true) { nodeCount } }
  135. { viewer { repositories(last:100) { edges { node { name

    issues(last:50) { edges { node { title } } } } } } } rateLimit(dryRun:true) { nodeCount } } { "data": { "rateLimit": { "nodeCount": 5100 } } }
  136. { viewer { repositories(last:100) { edges { node { name

    issues(last:50) { edges { node { title } } } } } } } rateLimit(dryRun:true) { cost } } { "data": { "rateLimit": { "cost": 1 } } }
  137. Schema Driven Development

  138. UI Development Staff Ship Production REST API

  139. but today…

  140. all new features are built with GraphQL

  141. from the start

  142. GraphQL Staff Ship Production REST API UI Development

  143. this allows us to build a true public API

  144. this allows us to build a true public API

  145. this allows us to build a true platform

  146. github/graphql-client

  147. exchange a query for Ruby objects

  148. collocate our queries in our views

  149. @result = GraphQL::Client.query(ProjectsQuery) <% @result.data.projects.each do |project| %> <h1><%= project.name

    %></h1> <% project.columns.each do |column| %> <p><%= column.name %></p> <p><%= column.cards.total_count %></p> <% column.cards.each do |card| %> <% card.title %> <p>Opened by <%= card.owner.login %></p> <% end %> <% end %> <% end %> Controller View
  150. GraphQL-backed REST APIs

  151. Staff Ship Production GraphQL UI Development REST API

  152. github/scientist

  153. https://api.github.com/user Legacy REST New GraphQL Return result Compare return values

  154. ensures data accuracy

  155. provides speed comparisons

  156. gjtorikian/graphql-docs

  157. https://github.com/gjtorikian/graphql-docs

  158. https://github.com/gjtorikian/graphql-docs

  159. automated changelog

  160. https://developer.github.com/v4/changelog

  161. community site

  162. None
  163. What’s next for the GraphQL API

  164. confidence in schema coverage

  165. GitHub Apps integration

  166. More information

  167. https://developer.github.com

  168. http://graphql.org

  169. http://platform.github.community

  170. thank you @bswinnerton