Launching GitHub's Public GraphQL API

Launching GitHub's Public GraphQL API

359024e7132672aaeef4a3e792be4ae5?s=128

Brooks Swinnerton

May 21, 2017
Tweet

Transcript

  1. + a

  2. Hi, I’m Brooks

  3. I work at !

  4. let’s talk about launching the GitHub GraphQL API

  5. How our GraphQL API came to be

  6. March 20th, 2016 proposal submitted

  7. we had dreams of APIv4

  8. multiple resources in one roundtrip

  9. schema introspection

  10. April 6th, 2016 proof of concept done

  11. { current_user { login repositories(affiliation: "owner") { id name }

    } }
  12. April 12th, 2016 New team created

  13. September 14th, 2016 early access

  14. Today >100 million queries/day

  15. We learned some things along the way

  16. Tooling

  17. documentation

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

  19. GraphiQL all-in-one

  20. "but we’re going to need a Ruby client"

  21. github/graphql-client

  22. objects in exchange for a query

  23. collocate our queries with our views

  24. but in Rails

  25. query profiling

  26. query { repository(owner:"rails", name:"rails") { viewerHasStarred } }

  27. { "data": { "repository": { "viewerHasStarred": false } }, "extensions":

    { "totalDuration": 42.06737782806158, "trackedAssociations": {}, "profiling": { "Repository:viewerHasStarred": { "type": "Boolean!", "sql": [ { "duration": 2.07, "sql": "SELECT 1 AS one FROM `repositories` INNER JOIN `stars` ON `repositories`.`id` = `stars`.`starrable_id` WHERE `stars`.`user_id` = 934497 AND `stars`.`starrable_type` = 'Repository' AND `repositories`.`id` = 8514 LIMIT 1 " } ] } } } }
  28. Authorization

  29. reusing the OAuth logic from our REST API

  30. OAuth scopes are granted to a token

  31. token is used to make a request

  32. familiar to our users

  33. less for us to build

  34. Organization = GraphQL::ObjectType.define do name "Organization" accepted_scopes ["read:org", "admin:org"] end

  35. Organization = GraphQL::ObjectType.define do name "Organization" accepted_scopes ["read:org", "admin:org"] end

  36. but with ✨ GraphQL ✨…

  37. we can analyze the query before resolution

  38. query { organization(login:"github") { members { totalCount } } }

  39. query { organization(login:"github") { members { totalCount } } }

  40. Organization = GraphQL::ObjectType.define do name "Organization" accepted_scopes ["read:org", "admin:org"] end

  41. but this isn’t perfect

  42. in some cases you need to perform resolution first

  43. repo vs public_repo

  44. we’ve introduced an authz layer for resolution

  45. Schema design

  46. first off

  47. there’s more than one

  48. one for new & sensitive features

  49. one for everyone else

  50. Organization = GraphQL::ObjectType.define do name "Organization" accepted_scopes ["read:org"] end

  51. Organization = GraphQL::ObjectType.define do name "Organization" accepted_scopes ["read:org"] visibility :public

    end
  52. Organization = GraphQL::ObjectType.define do name "Organization" accepted_scopes ["read:org"] visibility :public

    end
  53. CoolNewFeature = GraphQL::ObjectType.define do name "CoolNewFeature" accepted_scopes ["repo"] visibility :internal

    end
  54. mandatory first/last arguments on connections

  55. query { viewer { repositories(last:30) { edges { node {

    name } } } } }
  56. query { viewer { repositories(last:30) { edges { node {

    name } } } } }
  57. is/has/can prefix

  58. query { repository(owner:"rails", name:"rails") { isFork hasIssuesEnabled viewerCanAdminister } }

  59. query { repository(owner:"rails", name:"rails") { isFork hasIssuesEnabled viewerCanAdminister } }

  60. avoiding fields that should be types

  61. query { repository(owner:"rails", name:"rails") { ownerLogin } }

  62. query { repository(owner:"rails", name:"rails") { ownerLogin } }

  63. query { repository(owner:"rails",name:"rails") { owner { login } } }

  64. Feature Parity

  65. schema driven development* *stay tuned!

  66. with our REST API

  67. new features were developed for the UI

  68. then staff-shipped

  69. then released

  70. REST API work started after the ship

  71. but, today…

  72. all new features are built with GraphQL

  73. from the start

  74. CoolNewFeature = GraphQL::ObjectType.define do name "CoolNewFeature" accepted_scopes ["repo"] visibility :internal

    end
  75. CoolNewFeature = GraphQL::ObjectType.define do name "CoolNewFeature" accepted_scopes ["repo"] visibility :internal

    end
  76. CoolNewFeature = GraphQL::ObjectType.define do name "CoolNewFeature" accepted_scopes ["repo"] visibility :public

    end
  77. this allows us to build a true public API

  78. this allows us to build a true public API

  79. this allows us to build a true platform

  80. shared between GitHubbers and integrators

  81. but change is scary

  82. GraphQL-backed REST APIs

  83. this works great for new features

  84. but what about legacy features?

  85. GET https://api.github.com/user

  86. enter Scientist

  87. github/scientist

  88. measure data discrepancies

  89. measure the difference in performance

  90. None
  91. Where we’re headed

  92. static analysis of schema during code review

  93. rate limiting

  94. expose global relay IDs in REST API

  95. preview new fields and objects with headers

  96. Thank you
 @bswinnerton on Twitter & GitHub @brooks on Slack