A place to cache linked articles (think custom and personal wayback machine)
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

index.md 21KB

před 4 roky
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. title: The GitHub GraphQL API
  2. url: http://githubengineering.com/the-github-graphql-api/
  3. hash_url: d9457ea0fba660a5011e8671f44f212c
  4. <p>GitHub announced a public API <a href="https://github.com/blog/21-the-api">one month after the site launched</a>. We’ve evolved this platform through three versions, adhering to RFC standards and embracing new design patterns to provide a clear and consistent interface. We’ve often heard that our REST API was an inspiration for other companies; countless tutorials refer to our endpoints. Today, we’re excited to announce our biggest change to the API since we snubbed XML in favor of JSON: we’re making the GitHub API available through GraphQL.</p>
  5. <p>GraphQL is, at its core, a specification for a data querying language. We’d like to talk a bit about GraphQL, including the problems we believe it solves and the opportunities it provides to integrators.</p>
  6. <h2 id="why">Why?</h2>
  7. <p>You may be wondering why we chose to start supporting GraphQL. Our API was designed to be RESTful and hypermedia-driven. We’re fortunate to have <a href="https://developer.github.com/libraries/">dozens of different open-source clients</a> written in a plethora of languages. Businesses grew around these endpoints.</p>
  8. <p>Like most technology, REST is not perfect and has some drawbacks. Our ambition to change our API focused on solving two problems.</p>
  9. <p>The first was scalability. The REST API is responsible for over 60% of the requests made to our database tier. This is partly because, by its nature, hypermedia navigation requires a client to repeatedly communicate with a server so that it can get all the information it needs. Our responses were bloated and filled with all sorts of <code class="highlighter-rouge">*_url</code> hints in the JSON responses to help people continue to navigate through the API to get what they needed. Despite all the information we provided, we heard from integrators that our REST API also wasn’t very flexible. It sometimes required two or three separate calls to assemble a complete view of a resource. It seemed like our responses simultaneously sent too much data <em>and</em> didn’t include data that consumers needed.</p>
  10. <p>As we began to audit our endpoints in preparation for an APIv4, we encountered our second problem. We wanted to collect some meta-information about our endpoints. For example, we wanted to identify the OAuth scopes required for each endpoint. We wanted to be smarter about how our resources were paginated. We wanted assurances of type-safety for user-supplied parameters. We wanted to generate documentation from our code. We wanted to generate clients instead of manually supplying patches to <a href="http://octokit.github.io/">our Octokit suite</a>. We studied a variety of API specifications built to make some of this easier, but we found that none of the standards totally matched our requirements.</p>
  11. <p>And then we learned about GraphQL.</p>
  12. <h2 id="the-switch">The switch</h2>
  13. <p><a href="http://graphql.org/">GraphQL</a> is a querying language developed by Facebook over the course of several years. In essence, you construct your request by defining the resources you want. You send this via a <code class="highlighter-rouge">POST</code> to a server, and the response matches the format of your request.</p>
  14. <p>For example, say you wanted to fetch just a few attributes off of a user. Your GraphQL query might look like this:</p>
  15. <pre><code class="language-graphql">{
  16. viewer {
  17. login
  18. bio
  19. location
  20. isBountyHunter
  21. }
  22. }
  23. </code></pre>
  24. <p>And the response back might look like this:</p>
  25. <div class="language-json highlighter-rouge">
  26. <pre class="highlight"><code><span class="p">{</span><span class="w">
  27. </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  28. </span><span class="nt">"viewer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  29. </span><span class="nt">"login"</span><span class="p">:</span><span class="w"> </span><span class="s2">"octocat"</span><span class="p">,</span><span class="w">
  30. </span><span class="nt">"bio"</span><span class="p">:</span><span class="w"> </span><span class="s2">"I've been around the world, from London to the Bay."</span><span class="p">,</span><span class="w">
  31. </span><span class="nt">"location"</span><span class="p">:</span><span class="w"> </span><span class="s2">"San Francisco, CA"</span><span class="p">,</span><span class="w">
  32. </span><span class="nt">"isBountyHunter"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
  33. </span><span class="p">}</span><span class="w">
  34. </span><span class="p">}</span><span class="w">
  35. </span><span class="p">}</span><span class="w">
  36. </span></code></pre>
  37. </div>
  38. <p>You can see that the keys and values in the JSON response match right up with the terms in the query string.</p>
  39. <p>What if you wanted something more complicated? Let’s say you wanted to know how many repositories you’ve starred. You also want to get the names of your first three repositories, as well as their total number of stars, total number of forks, total number of watchers, and total number of open issues. That query might look like this:</p>
  40. <pre><code class="language-graphql">{
  41. viewer {
  42. login
  43. starredRepositories {
  44. totalCount
  45. }
  46. repositories(first: 3) {
  47. edges {
  48. node {
  49. name
  50. stargazers {
  51. totalCount
  52. }
  53. forks {
  54. totalCount
  55. }
  56. watchers {
  57. totalCount
  58. }
  59. issues(states:[OPEN]) {
  60. totalCount
  61. }
  62. }
  63. }
  64. }
  65. }
  66. }
  67. </code></pre>
  68. <p>The response from our API might be:</p>
  69. <div class="language-json highlighter-rouge">
  70. <pre class="highlight"><code><span class="p">{</span><span class="w">
  71. </span><span class="nt">"data"</span><span class="p">:{</span><span class="w">
  72. </span><span class="nt">"viewer"</span><span class="p">:{</span><span class="w">
  73. </span><span class="nt">"login"</span><span class="p">:</span><span class="w"> </span><span class="s2">"octocat"</span><span class="p">,</span><span class="w">
  74. </span><span class="nt">"starredRepositories"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  75. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">131</span><span class="w">
  76. </span><span class="p">},</span><span class="w">
  77. </span><span class="nt">"repositories"</span><span class="p">:{</span><span class="w">
  78. </span><span class="nt">"edges"</span><span class="p">:[</span><span class="w">
  79. </span><span class="p">{</span><span class="w">
  80. </span><span class="nt">"node"</span><span class="p">:{</span><span class="w">
  81. </span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"octokit.rb"</span><span class="p">,</span><span class="w">
  82. </span><span class="nt">"stargazers"</span><span class="p">:{</span><span class="w">
  83. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">17</span><span class="w">
  84. </span><span class="p">},</span><span class="w">
  85. </span><span class="nt">"forks"</span><span class="p">:{</span><span class="w">
  86. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="w">
  87. </span><span class="p">},</span><span class="w">
  88. </span><span class="nt">"watchers"</span><span class="p">:{</span><span class="w">
  89. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="w">
  90. </span><span class="p">},</span><span class="w">
  91. </span><span class="nt">"issues"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  92. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
  93. </span><span class="p">}</span><span class="w">
  94. </span><span class="p">}</span><span class="w">
  95. </span><span class="p">},</span><span class="w">
  96. </span><span class="p">{</span><span class="w">
  97. </span><span class="nt">"node"</span><span class="p">:{</span><span class="w">
  98. </span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"octokit.objc"</span><span class="p">,</span><span class="w">
  99. </span><span class="nt">"stargazers"</span><span class="p">:{</span><span class="w">
  100. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="w">
  101. </span><span class="p">},</span><span class="w">
  102. </span><span class="nt">"forks"</span><span class="p">:{</span><span class="w">
  103. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w">
  104. </span><span class="p">},</span><span class="w">
  105. </span><span class="nt">"watchers"</span><span class="p">:{</span><span class="w">
  106. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
  107. </span><span class="p">},</span><span class="w">
  108. </span><span class="nt">"issues"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  109. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="w">
  110. </span><span class="p">}</span><span class="w">
  111. </span><span class="p">}</span><span class="w">
  112. </span><span class="p">},</span><span class="w">
  113. </span><span class="p">{</span><span class="w">
  114. </span><span class="nt">"node"</span><span class="p">:{</span><span class="w">
  115. </span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"octokit.net"</span><span class="p">,</span><span class="w">
  116. </span><span class="nt">"stargazers"</span><span class="p">:{</span><span class="w">
  117. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">19</span><span class="w">
  118. </span><span class="p">},</span><span class="w">
  119. </span><span class="nt">"forks"</span><span class="p">:{</span><span class="w">
  120. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">7</span><span class="w">
  121. </span><span class="p">},</span><span class="w">
  122. </span><span class="nt">"watchers"</span><span class="p">:{</span><span class="w">
  123. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="w">
  124. </span><span class="p">},</span><span class="w">
  125. </span><span class="nt">"issues"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  126. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">4</span><span class="w">
  127. </span><span class="p">}</span><span class="w">
  128. </span><span class="p">}</span><span class="w">
  129. </span><span class="p">}</span><span class="w">
  130. </span><span class="p">]</span><span class="w">
  131. </span><span class="p">}</span><span class="w">
  132. </span><span class="p">}</span><span class="w">
  133. </span><span class="p">}</span><span class="w">
  134. </span><span class="p">}</span><span class="w">
  135. </span></code></pre>
  136. </div>
  137. <p>You just made <em>one</em> request to fetch all the data you wanted.</p>
  138. <p>This type of design enables clients where smaller payload sizes are essential. For example, a mobile app could simplify its requests by only asking for the data it needs. This enables new possibilities and workflows that are freed from the limitations of downloading and parsing massive JSON blobs.</p>
  139. <p>Query analysis is something that we’re also exploring with. Based on the resources that are requested, we can start providing more intelligent information to clients. For example, say you’ve made the following request:</p>
  140. <pre><code class="language-graphql">{
  141. viewer {
  142. login
  143. email
  144. }
  145. }
  146. </code></pre>
  147. <p>Before executing the request, the GraphQL server notes that you’re trying to get the <code class="highlighter-rouge">email</code> field. If your client is misconfigured, a response back from our server might look like this:</p>
  148. <div class="language-json highlighter-rouge">
  149. <pre class="highlight"><code><span class="p">{</span><span class="w">
  150. </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  151. </span><span class="nt">"viewer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  152. </span><span class="nt">"login"</span><span class="p">:</span><span class="w"> </span><span class="s2">"octocat"</span><span class="w">
  153. </span><span class="p">}</span><span class="w">
  154. </span><span class="p">},</span><span class="w">
  155. </span><span class="nt">"errors"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
  156. </span><span class="p">{</span><span class="w">
  157. </span><span class="nt">"message"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Your token has not been granted the required scopes to
  158. execute this query. The 'email' field requires one of the following
  159. scopes: ['user'], but your token has only been granted the: ['gist']
  160. scopes. Please modify your token's scopes at: https://github.com/settings/tokens."</span><span class="w">
  161. </span><span class="p">}</span><span class="w">
  162. </span><span class="p">]</span><span class="w">
  163. </span><span class="p">}</span><span class="w">
  164. </span></code></pre>
  165. </div>
  166. <p>This could be beneficial for users concerned about the OAuth scopes required by integrators. Insight into the scopes required could ensure that only the appropriate types are being requested.</p>
  167. <p>There are several other features of GraphQL that we hope to make available to clients, such as:</p>
  168. <ul>
  169. <li>The ability to <em>batch requests</em>, where you can define dependencies between two separate queries and fetch data efficiently.</li>
  170. <li>The ability to <em>create subscriptions</em>, where your client can receive new data when it becomes available.</li>
  171. <li>The ability to <em>defer data</em>, where you choose to mark a part of your response as time-insensitive.</li>
  172. </ul>
  173. <h2 id="defining-the-schema">Defining the schema</h2>
  174. <p>In order to determine if GraphQL really was a technology we wanted to embrace, we formed a small team within the broader Platform organization and went looking for a feature on the site we wanted to build using GraphQL. We decided that implementing <a href="https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments">emoji reactions on comments</a> was concise enough to try and port to GraphQL. Choosing a subset of the site to power with GraphQL required us to model a complete workflow and focus on building the new objects and types that defined our GraphQL schema. For example, we started by constructing a user in our schema, moved on to a repository, and then expanded to issues within a repository. Over time, we grew the schema to encapsulate all the actions necessary for modeling reactions.</p>
  175. <p>We found implementing a GraphQL server to be very straightforward. <a href="https://facebook.github.io/graphql/">The Spec</a> is clearly written and succinctly describes the behaviors of various parts of a schema. GraphQL has a type system that forces the server to be unambiguous about requests it receives and responses it produces. You define a schema, describing the objects that represent your resources, fields on those objects, and the connections between various objects. For example, a <code class="highlighter-rouge">Repository</code> object has a non-null <code class="highlighter-rouge">String</code> field for the <code class="highlighter-rouge">name</code>. A repository also has <code class="highlighter-rouge">watchers</code>, which is a connection to another non-nullable object, <code class="highlighter-rouge">User</code>.</p>
  176. <p>Although the initial team exploring GraphQL worked mostly on the backend, we had several allies on the frontend who were also interested in GraphQL, and, specifically, moving parts of GitHub to use <a href="https://facebook.github.io/relay/">Relay</a>. They too were seeking better ways to access user data and present it more efficiently on the website. We began to work together to continue finding portions of the site that would be easy to communicate with via our nascent GraphQL schema. We decided to begin transforming some of our social features, such as the profile page, the stars counter, and the ability to watch repositories. These initial explorations paved the way to placing GraphQL in production. (That’s right! We’ve been running GraphQL in production for some time now.) As time went on, we began to get a bit more ambitious: we ported over some of the Git commit history pages to GraphQL and used <a href="http://githubengineering.com/scientist/">Scientist</a> to identify any potential discrepancies.</p>
  177. <p>Drawing off our experiences in supporting the REST API, we worked quickly to implement our existing services to work with GraphQL. This included setting up logging requests and reporting exceptions, OAuth and AuthZ access, rate limiting, and providing helpful error responses. We tested our schema to ensure that every part of was documented and we wrote linters to ensure that our naming structure was standardized.</p>
  178. <h2 id="open-source">Open source</h2>
  179. <p>We work primarily in Ruby, and we were grateful for the existing gems supporting GraphQL. We used the <a href="https://github.com/rmosolgo/graphql-ruby">rmosolgo/graphql-ruby</a> gem to implement <strong>the entirety</strong> of our schema. We also incorporated the <a href="https://github.com/Shopify/graphql-batch">Shopify/graphql-batch</a> gem to ensure that multiple records and relationships were fetched efficiently.</p>
  180. <p>Our frontend and backend engineers were also able to contribute to these gems as we experimented with them. We’re thankful to the maintainers for their very quick work in accepting our patches. To that end, we’d like to humbly offer a couple of our own open source projects:</p>
  181. <p>We’re going to continue to extract more parts of our system that we’ve developed internally and release them as open source software, such as our loaders that efficiently batch ActiveRecord requests.</p>
  182. <h2 id="the-future">The future</h2>
  183. <p>The move to GraphQL marks a larger shift in our Platform strategy to be more transparent and more flexible. Over the next year, we’re going to keep iterating on our schema to bring it out of Early Access and into a wider production readiness.</p>
  184. <p>Since our application engineers are using the same GraphQL platform that we’re making available to our integrators, this provides us with the opportunity to ship UI features <em>in conjunction with</em> API access. Our new Projects feature is a good example of this: the UI on the site is powered by GraphQL, and you can already use the feature programmatically. Using GraphQL on the frontend and backend eliminates the gap between what we release and what you can consume. We really look forward to making more of these simultaneous releases.</p>
  185. <p>GraphQL represents a massive leap forward for API development. Type safety, introspection, generated documentation, and predictable responses benefit both the maintainers and consumers of our platform. We’re looking forward to our new era of a GraphQL-backed platform, and we hope that you do, too!</p>
  186. <p>If you’d like to get started with GraphQL—including our new GraphQL Explorer that lets you make <img class="emoji" title=":sparkles:" alt=":sparkles:" src="https://assets-cdn.github.com/images/icons/emoji/unicode/2728.png" align="absmiddle"/>live queries<img class="emoji" title=":sparkles:" alt=":sparkles:" src="https://assets-cdn.github.com/images/icons/emoji/unicode/2728.png" align="absmiddle"/>, check out <a href="https://developer.github.com/early-access/graphql">our developer documentation</a>!</p>