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

Launching GitHub's Public GraphQL API

Launching GitHub's Public GraphQL API

Brooks Swinnerton

May 21, 2017
Tweet

More Decks by Brooks Swinnerton

Other Decks in Technology

Transcript

  1. + a

    View Slide

  2. Hi, I’m Brooks

    View Slide

  3. I work at !

    View Slide

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

    View Slide

  5. How our GraphQL API
    came to be

    View Slide

  6. March 20th, 2016
    proposal submitted

    View Slide

  7. we had dreams of APIv4

    View Slide

  8. multiple resources in
    one roundtrip

    View Slide

  9. schema introspection

    View Slide

  10. April 6th, 2016
    proof of concept done

    View Slide

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

    View Slide

  12. April 12th, 2016
    New team created

    View Slide

  13. September 14th, 2016
    early access

    View Slide

  14. Today
    >100 million queries/day

    View Slide

  15. We learned some things
    along the way

    View Slide

  16. Tooling

    View Slide

  17. documentation

    View Slide

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

    View Slide

  19. GraphiQL all-in-one

    View Slide

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

    View Slide

  21. github/graphql-client

    View Slide

  22. objects in exchange
    for a query

    View Slide

  23. collocate our queries
    with our views

    View Slide

  24. but in Rails

    View Slide

  25. query profiling

    View Slide

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

    View Slide

  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 "
    }
    ]
    }
    }
    }
    }

    View Slide

  28. Authorization

    View Slide

  29. reusing the OAuth logic
    from our REST API

    View Slide

  30. OAuth scopes are
    granted to a token

    View Slide

  31. token is used to
    make a request

    View Slide

  32. familiar to our users

    View Slide

  33. less for us to build

    View Slide

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

    View Slide

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

    View Slide

  36. but with
    ✨ GraphQL ✨…

    View Slide

  37. we can analyze the query
    before resolution

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  41. but this isn’t perfect

    View Slide

  42. in some cases you need to
    perform resolution first

    View Slide

  43. repo vs public_repo

    View Slide

  44. we’ve introduced an authz
    layer for resolution

    View Slide

  45. Schema design

    View Slide

  46. first off

    View Slide

  47. there’s more than one

    View Slide

  48. one for new & sensitive
    features

    View Slide

  49. one for everyone else

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  54. mandatory first/last
    arguments on connections

    View Slide

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

    View Slide

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

    View Slide

  57. is/has/can prefix

    View Slide

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

    View Slide

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

    View Slide

  60. avoiding fields that
    should be types

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  64. Feature Parity

    View Slide

  65. schema driven
    development*
    *stay tuned!

    View Slide

  66. with our REST API

    View Slide

  67. new features were
    developed for the UI

    View Slide

  68. then staff-shipped

    View Slide

  69. then released

    View Slide

  70. REST API work started
    after the ship

    View Slide

  71. but, today…

    View Slide

  72. all new features are
    built with GraphQL

    View Slide

  73. from the start

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  77. this allows us to build a
    true public API

    View Slide

  78. this allows us to build a
    true public API

    View Slide

  79. this allows us to build a
    true platform

    View Slide

  80. shared between
    GitHubbers and integrators

    View Slide

  81. but change is scary

    View Slide

  82. GraphQL-backed
    REST APIs

    View Slide

  83. this works great for
    new features

    View Slide

  84. but what about
    legacy features?

    View Slide

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

    View Slide

  86. enter Scientist

    View Slide

  87. github/scientist

    View Slide

  88. measure data
    discrepancies

    View Slide

  89. measure the difference
    in performance

    View Slide

  90. View Slide

  91. Where we’re headed

    View Slide

  92. static analysis of schema
    during code review

    View Slide

  93. rate limiting

    View Slide

  94. expose global relay IDs in
    REST API

    View Slide

  95. preview new fields and
    objects with headers

    View Slide

  96. Thank you

    @bswinnerton on Twitter & GitHub
    @brooks on Slack

    View Slide