A place to cache linked articles (think custom and personal wayback machine)
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

index.html 37KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang=fr>
  4. <!-- Has to be within the first 1024 bytes, hence before the <title>
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset=utf-8>
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name=viewport content="width=device-width,minimum-scale=1,initial-scale=1,shrink-to-fit=no">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>The GitHub GraphQL API (archive) — David Larlet</title>
  13. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  14. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons/apple-touch-icon.png">
  15. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons/favicon-32x32.png">
  16. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons/favicon-16x16.png">
  17. <link rel="manifest" href="/manifest.json">
  18. <link rel="mask-icon" href="/static/david/icons/safari-pinned-tab.svg" color="#5bbad5">
  19. <link rel="shortcut icon" href="/static/david/icons/favicon.ico">
  20. <meta name="apple-mobile-web-app-title" content="David Larlet">
  21. <meta name="application-name" content="David Larlet">
  22. <meta name="msapplication-TileColor" content="#da532c">
  23. <meta name="msapplication-config" content="/static/david/icons/browserconfig.xml">
  24. <meta name="theme-color" content="#f0f0ea">
  25. <!-- That good ol' feed, subscribe :p. -->
  26. <link rel=alternate type="application/atom+xml" title=Feed href="/david/log/">
  27. <meta name="robots" content="noindex, nofollow">
  28. <meta content="origin-when-cross-origin" name="referrer">
  29. <!-- Canonical URL for SEO purposes -->
  30. <link rel="canonical" href="http://githubengineering.com/the-github-graphql-api/">
  31. <style>
  32. /* http://meyerweb.com/eric/tools/css/reset/ */
  33. html, body, div, span,
  34. h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  35. a, abbr, address, big, cite, code,
  36. del, dfn, em, img, ins,
  37. small, strike, strong, tt, var,
  38. dl, dt, dd, ol, ul, li,
  39. fieldset, form, label, legend,
  40. table, caption, tbody, tfoot, thead, tr, th, td,
  41. article, aside, canvas, details, embed,
  42. figure, figcaption, footer, header, hgroup,
  43. menu, nav, output, ruby, section, summary,
  44. time, mark, audio, video {
  45. margin: 0;
  46. padding: 0;
  47. border: 0;
  48. font-size: 100%;
  49. font: inherit;
  50. vertical-align: baseline;
  51. }
  52. /* HTML5 display-role reset for older browsers */
  53. article, aside, details, figcaption, figure,
  54. footer, header, hgroup, menu, nav, section { display: block; }
  55. body { line-height: 1; }
  56. blockquote, q { quotes: none; }
  57. blockquote:before, blockquote:after,
  58. q:before, q:after {
  59. content: '';
  60. content: none;
  61. }
  62. table {
  63. border-collapse: collapse;
  64. border-spacing: 0;
  65. }
  66. /* http://practicaltypography.com/equity.html */
  67. /* https://calendar.perfplanet.com/2016/no-font-face-bulletproof-syntax/ */
  68. /* https://www.filamentgroup.com/lab/js-web-fonts.html */
  69. @font-face {
  70. font-family: 'EquityTextB';
  71. src: url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff2') format('woff2'),
  72. url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff') format('woff');
  73. font-weight: 300;
  74. font-style: normal;
  75. font-display: swap;
  76. }
  77. @font-face {
  78. font-family: 'EquityTextB';
  79. src: url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff2') format('woff2'),
  80. url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff') format('woff');
  81. font-weight: 300;
  82. font-style: italic;
  83. font-display: swap;
  84. }
  85. @font-face {
  86. font-family: 'EquityTextB';
  87. src: url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff2') format('woff2'),
  88. url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff') format('woff');
  89. font-weight: 700;
  90. font-style: normal;
  91. font-display: swap;
  92. }
  93. @font-face {
  94. font-family: 'ConcourseT3';
  95. src: url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff2') format('woff2'),
  96. url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff') format('woff');
  97. font-weight: 300;
  98. font-style: normal;
  99. font-display: swap;
  100. }
  101. /* http://practice.typekit.com/lesson/caring-about-opentype-features/ */
  102. body {
  103. /* http://www.cssfontstack.com/ Palatino 99% Win 86% Mac */
  104. font-family: "EquityTextB", Palatino, serif;
  105. background-color: #f0f0ea;
  106. color: #07486c;
  107. font-kerning: normal;
  108. -moz-osx-font-smoothing: grayscale;
  109. -webkit-font-smoothing: subpixel-antialiased;
  110. text-rendering: optimizeLegibility;
  111. font-variant-ligatures: common-ligatures contextual;
  112. font-feature-settings: "kern", "liga", "clig", "calt";
  113. }
  114. pre, code, kbd, samp, var, tt {
  115. font-family: 'TriplicateT4c', monospace;
  116. }
  117. em {
  118. font-style: italic;
  119. color: #323a45;
  120. }
  121. strong {
  122. font-weight: bold;
  123. color: black;
  124. }
  125. nav {
  126. background-color: #323a45;
  127. color: #f0f0ea;
  128. display: flex;
  129. justify-content: space-around;
  130. padding: 1rem .5rem;
  131. }
  132. nav:last-child {
  133. border-bottom: 1vh solid #2d7474;
  134. }
  135. nav a {
  136. color: #f0f0ea;
  137. }
  138. nav abbr {
  139. border-bottom: 1px dotted white;
  140. }
  141. h1 {
  142. border-top: 1vh solid #2d7474;
  143. border-bottom: .2vh dotted #2d7474;
  144. background-color: #e3e1e1;
  145. color: #323a45;
  146. text-align: center;
  147. padding: 5rem 0 4rem 0;
  148. width: 100%;
  149. font-family: 'ConcourseT3';
  150. display: flex;
  151. flex-direction: column;
  152. }
  153. h1.single {
  154. padding-bottom: 10rem;
  155. }
  156. h1 span {
  157. position: absolute;
  158. top: 1vh;
  159. left: 20%;
  160. line-height: 0;
  161. }
  162. h1 span a {
  163. line-height: 1.7;
  164. padding: 1rem 1.2rem .6rem 1.2rem;
  165. border-radius: 0 0 6% 6%;
  166. background: #2d7474;
  167. font-size: 1.3rem;
  168. color: white;
  169. text-decoration: none;
  170. }
  171. h2 {
  172. margin: 4rem 0 1rem;
  173. border-top: .2vh solid #2d7474;
  174. padding-top: 1vh;
  175. }
  176. h3 {
  177. text-align: center;
  178. margin: 3rem 0 .75em;
  179. }
  180. hr {
  181. height: .4rem;
  182. width: .4rem;
  183. border-radius: .4rem;
  184. background: #07486c;
  185. margin: 2.5rem auto;
  186. }
  187. time {
  188. display: bloc;
  189. margin-left: 0 !important;
  190. }
  191. ul, ol {
  192. margin: 2rem;
  193. }
  194. ul {
  195. list-style-type: square;
  196. }
  197. a {
  198. text-decoration-skip-ink: auto;
  199. text-decoration-thickness: 0.05em;
  200. text-underline-offset: 0.09em;
  201. }
  202. article {
  203. max-width: 50rem;
  204. display: flex;
  205. flex-direction: column;
  206. margin: 2rem auto;
  207. }
  208. article.single {
  209. border-top: .2vh dotted #2d7474;
  210. margin: -6rem auto 1rem auto;
  211. background: #f0f0ea;
  212. padding: 2rem;
  213. }
  214. article p:last-child {
  215. margin-bottom: 1rem;
  216. }
  217. p {
  218. padding: 0 .5rem;
  219. margin-left: 3rem;
  220. }
  221. p + p,
  222. figure + p {
  223. margin-top: 2rem;
  224. }
  225. blockquote {
  226. background-color: #e3e1e1;
  227. border-left: .5vw solid #2d7474;
  228. display: flex;
  229. flex-direction: column;
  230. align-items: center;
  231. padding: 1rem;
  232. margin: 1.5rem;
  233. }
  234. blockquote cite {
  235. font-style: italic;
  236. }
  237. blockquote p {
  238. margin-left: 0;
  239. }
  240. figure {
  241. border-top: .2vh solid #2d7474;
  242. background-color: #e3e1e1;
  243. text-align: center;
  244. padding: 1.5rem 0;
  245. margin: 1rem 0 0;
  246. font-size: 1.5rem;
  247. width: 100%;
  248. }
  249. figure img {
  250. max-width: 250px;
  251. max-height: 250px;
  252. border: .5vw solid #323a45;
  253. padding: 1px;
  254. }
  255. figcaption {
  256. padding: 1rem;
  257. line-height: 1.4;
  258. }
  259. aside {
  260. display: flex;
  261. flex-direction: column;
  262. background-color: #e3e1e1;
  263. padding: 1rem 0;
  264. border-bottom: .2vh solid #07486c;
  265. }
  266. aside p {
  267. max-width: 50rem;
  268. margin: 0 auto;
  269. }
  270. /* https://fvsch.com/code/css-locks/ */
  271. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  272. font-size: 1rem;
  273. line-height: calc( 1.5em + 0.2 * 1rem );
  274. }
  275. h1 {
  276. font-size: 1.9rem;
  277. line-height: calc( 1.2em + 0.2 * 1rem );
  278. }
  279. h2 {
  280. font-size: 1.6rem;
  281. line-height: calc( 1.3em + 0.2 * 1rem );
  282. }
  283. h3 {
  284. font-size: 1.35rem;
  285. line-height: calc( 1.4em + 0.2 * 1rem );
  286. }
  287. @media (min-width: 20em) {
  288. /* The (100vw - 20rem) / (50 - 20) part
  289. resolves to 0-1rem, depending on the
  290. viewport width (between 20em and 50em). */
  291. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  292. font-size: calc( 1rem + .6 * (100vw - 20rem) / (50 - 20) );
  293. line-height: calc( 1.5em + 0.2 * (100vw - 50rem) / (20 - 50) );
  294. margin-left: 0;
  295. }
  296. h1 {
  297. font-size: calc( 1.9rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  298. line-height: calc( 1.2em + 0.2 * (100vw - 50rem) / (20 - 50) );
  299. }
  300. h2 {
  301. font-size: calc( 1.5rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  302. line-height: calc( 1.3em + 0.2 * (100vw - 50rem) / (20 - 50) );
  303. }
  304. h3 {
  305. font-size: calc( 1.35rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  306. line-height: calc( 1.4em + 0.2 * (100vw - 50rem) / (20 - 50) );
  307. }
  308. }
  309. @media (min-width: 50em) {
  310. /* The right part of the addition *must* be a
  311. rem value. In this example we *could* change
  312. the whole declaration to font-size:2.5rem,
  313. but if our baseline value was not expressed
  314. in rem we would have to use calc. */
  315. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  316. font-size: calc( 1rem + .6 * 1rem );
  317. line-height: 1.5em;
  318. }
  319. p, li, pre, details {
  320. margin-left: 3rem;
  321. }
  322. h1 {
  323. font-size: calc( 1.9rem + 1.5 * 1rem );
  324. line-height: 1.2em;
  325. }
  326. h2 {
  327. font-size: calc( 1.5rem + 1.5 * 1rem );
  328. line-height: 1.3em;
  329. }
  330. h3 {
  331. font-size: calc( 1.35rem + 1.5 * 1rem );
  332. line-height: 1.4em;
  333. }
  334. figure img {
  335. max-width: 500px;
  336. max-height: 500px;
  337. }
  338. }
  339. figure.unsquared {
  340. margin-bottom: 1.5rem;
  341. }
  342. figure.unsquared img {
  343. height: inherit;
  344. }
  345. @media print {
  346. body { font-size: 100%; }
  347. a:after { content: " (" attr(href) ")"; }
  348. a, a:link, a:visited, a:after {
  349. text-decoration: underline;
  350. text-shadow: none !important;
  351. background-image: none !important;
  352. background: white;
  353. color: black;
  354. }
  355. abbr[title] { border-bottom: 0; }
  356. abbr[title]:after { content: " (" attr(title) ")"; }
  357. img { page-break-inside: avoid; }
  358. @page { margin: 2cm .5cm; }
  359. h1, h2, h3 { page-break-after: avoid; }
  360. p3 { orphans: 3; widows: 3; }
  361. img {
  362. max-width: 250px !important;
  363. max-height: 250px !important;
  364. }
  365. nav, aside { display: none; }
  366. }
  367. ul.with_columns {
  368. column-count: 1;
  369. }
  370. @media (min-width: 20em) {
  371. ul.with_columns {
  372. column-count: 2;
  373. }
  374. }
  375. @media (min-width: 50em) {
  376. ul.with_columns {
  377. column-count: 3;
  378. }
  379. }
  380. ul.with_two_columns {
  381. column-count: 1;
  382. }
  383. @media (min-width: 20em) {
  384. ul.with_two_columns {
  385. column-count: 1;
  386. }
  387. }
  388. @media (min-width: 50em) {
  389. ul.with_two_columns {
  390. column-count: 2;
  391. }
  392. }
  393. .gallery {
  394. display: flex;
  395. flex-wrap: wrap;
  396. justify-content: space-around;
  397. }
  398. .gallery figure img {
  399. margin-left: 1rem;
  400. margin-right: 1rem;
  401. }
  402. .gallery figure figcaption {
  403. font-family: 'ConcourseT3'
  404. }
  405. footer {
  406. font-family: 'ConcourseT3';
  407. display: flex;
  408. flex-direction: column;
  409. border-top: 3px solid white;
  410. padding: 4rem 0;
  411. background-color: #07486c;
  412. color: white;
  413. }
  414. footer > * {
  415. max-width: 50rem;
  416. margin: 0 auto;
  417. }
  418. footer a {
  419. color: #f1c40f;
  420. }
  421. footer .avatar {
  422. width: 200px;
  423. height: 200px;
  424. border-radius: 50%;
  425. float: left;
  426. -webkit-shape-outside: circle();
  427. shape-outside: circle();
  428. margin-right: 2rem;
  429. padding: 2px 5px 5px 2px;
  430. background: white;
  431. border-left: 1px solid #f1c40f;
  432. border-top: 1px solid #f1c40f;
  433. border-right: 5px solid #f1c40f;
  434. border-bottom: 5px solid #f1c40f;
  435. }
  436. </style>
  437. <h1>
  438. <span><a id="jumper" href="#jumpto" title="Un peu perdu ?">?</a></span>
  439. The GitHub GraphQL API (archive)
  440. <time>Pour la pérennité des contenus liés. Non-indexé, retrait sur simple email.</time>
  441. </h1>
  442. <section>
  443. <article>
  444. <h3><a href="http://githubengineering.com/the-github-graphql-api/">Source originale du contenu</a></h3>
  445. <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>
  446. <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>
  447. <h2 id="why">Why?</h2>
  448. <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>
  449. <p>Like most technology, REST is not perfect and has some drawbacks. Our ambition to change our API focused on solving two problems.</p>
  450. <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>
  451. <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>
  452. <p>And then we learned about GraphQL.</p>
  453. <h2 id="the-switch">The switch</h2>
  454. <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>
  455. <p>For example, say you wanted to fetch just a few attributes off of a user. Your GraphQL query might look like this:</p>
  456. <pre><code class="language-graphql">{
  457. viewer {
  458. login
  459. bio
  460. location
  461. isBountyHunter
  462. }
  463. }
  464. </code></pre>
  465. <p>And the response back might look like this:</p>
  466. <div class="language-json highlighter-rouge">
  467. <pre class="highlight"><code><span class="p">{</span><span class="w">
  468. </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  469. </span><span class="nt">"viewer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  470. </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">
  471. </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">
  472. </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">
  473. </span><span class="nt">"isBountyHunter"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
  474. </span><span class="p">}</span><span class="w">
  475. </span><span class="p">}</span><span class="w">
  476. </span><span class="p">}</span><span class="w">
  477. </span></code></pre>
  478. </div>
  479. <p>You can see that the keys and values in the JSON response match right up with the terms in the query string.</p>
  480. <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>
  481. <pre><code class="language-graphql">{
  482. viewer {
  483. login
  484. starredRepositories {
  485. totalCount
  486. }
  487. repositories(first: 3) {
  488. edges {
  489. node {
  490. name
  491. stargazers {
  492. totalCount
  493. }
  494. forks {
  495. totalCount
  496. }
  497. watchers {
  498. totalCount
  499. }
  500. issues(states:[OPEN]) {
  501. totalCount
  502. }
  503. }
  504. }
  505. }
  506. }
  507. }
  508. </code></pre>
  509. <p>The response from our API might be:</p>
  510. <div class="language-json highlighter-rouge">
  511. <pre class="highlight"><code><span class="p">{</span><span class="w">
  512. </span><span class="nt">"data"</span><span class="p">:{</span><span class="w">
  513. </span><span class="nt">"viewer"</span><span class="p">:{</span><span class="w">
  514. </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">
  515. </span><span class="nt">"starredRepositories"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  516. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">131</span><span class="w">
  517. </span><span class="p">},</span><span class="w">
  518. </span><span class="nt">"repositories"</span><span class="p">:{</span><span class="w">
  519. </span><span class="nt">"edges"</span><span class="p">:[</span><span class="w">
  520. </span><span class="p">{</span><span class="w">
  521. </span><span class="nt">"node"</span><span class="p">:{</span><span class="w">
  522. </span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"octokit.rb"</span><span class="p">,</span><span class="w">
  523. </span><span class="nt">"stargazers"</span><span class="p">:{</span><span class="w">
  524. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">17</span><span class="w">
  525. </span><span class="p">},</span><span class="w">
  526. </span><span class="nt">"forks"</span><span class="p">:{</span><span class="w">
  527. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="w">
  528. </span><span class="p">},</span><span class="w">
  529. </span><span class="nt">"watchers"</span><span class="p">:{</span><span class="w">
  530. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="w">
  531. </span><span class="p">},</span><span class="w">
  532. </span><span class="nt">"issues"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  533. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
  534. </span><span class="p">}</span><span class="w">
  535. </span><span class="p">}</span><span class="w">
  536. </span><span class="p">},</span><span class="w">
  537. </span><span class="p">{</span><span class="w">
  538. </span><span class="nt">"node"</span><span class="p">:{</span><span class="w">
  539. </span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"octokit.objc"</span><span class="p">,</span><span class="w">
  540. </span><span class="nt">"stargazers"</span><span class="p">:{</span><span class="w">
  541. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">2</span><span class="w">
  542. </span><span class="p">},</span><span class="w">
  543. </span><span class="nt">"forks"</span><span class="p">:{</span><span class="w">
  544. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w">
  545. </span><span class="p">},</span><span class="w">
  546. </span><span class="nt">"watchers"</span><span class="p">:{</span><span class="w">
  547. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w">
  548. </span><span class="p">},</span><span class="w">
  549. </span><span class="nt">"issues"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  550. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="w">
  551. </span><span class="p">}</span><span class="w">
  552. </span><span class="p">}</span><span class="w">
  553. </span><span class="p">},</span><span class="w">
  554. </span><span class="p">{</span><span class="w">
  555. </span><span class="nt">"node"</span><span class="p">:{</span><span class="w">
  556. </span><span class="nt">"name"</span><span class="p">:</span><span class="s2">"octokit.net"</span><span class="p">,</span><span class="w">
  557. </span><span class="nt">"stargazers"</span><span class="p">:{</span><span class="w">
  558. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">19</span><span class="w">
  559. </span><span class="p">},</span><span class="w">
  560. </span><span class="nt">"forks"</span><span class="p">:{</span><span class="w">
  561. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">7</span><span class="w">
  562. </span><span class="p">},</span><span class="w">
  563. </span><span class="nt">"watchers"</span><span class="p">:{</span><span class="w">
  564. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">3</span><span class="w">
  565. </span><span class="p">},</span><span class="w">
  566. </span><span class="nt">"issues"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  567. </span><span class="nt">"totalCount"</span><span class="p">:</span><span class="w"> </span><span class="mi">4</span><span class="w">
  568. </span><span class="p">}</span><span class="w">
  569. </span><span class="p">}</span><span class="w">
  570. </span><span class="p">}</span><span class="w">
  571. </span><span class="p">]</span><span class="w">
  572. </span><span class="p">}</span><span class="w">
  573. </span><span class="p">}</span><span class="w">
  574. </span><span class="p">}</span><span class="w">
  575. </span><span class="p">}</span><span class="w">
  576. </span></code></pre>
  577. </div>
  578. <p>You just made <em>one</em> request to fetch all the data you wanted.</p>
  579. <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>
  580. <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>
  581. <pre><code class="language-graphql">{
  582. viewer {
  583. login
  584. email
  585. }
  586. }
  587. </code></pre>
  588. <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>
  589. <div class="language-json highlighter-rouge">
  590. <pre class="highlight"><code><span class="p">{</span><span class="w">
  591. </span><span class="nt">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  592. </span><span class="nt">"viewer"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
  593. </span><span class="nt">"login"</span><span class="p">:</span><span class="w"> </span><span class="s2">"octocat"</span><span class="w">
  594. </span><span class="p">}</span><span class="w">
  595. </span><span class="p">},</span><span class="w">
  596. </span><span class="nt">"errors"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
  597. </span><span class="p">{</span><span class="w">
  598. </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
  599. execute this query. The 'email' field requires one of the following
  600. scopes: ['user'], but your token has only been granted the: ['gist']
  601. scopes. Please modify your token's scopes at: https://github.com/settings/tokens."</span><span class="w">
  602. </span><span class="p">}</span><span class="w">
  603. </span><span class="p">]</span><span class="w">
  604. </span><span class="p">}</span><span class="w">
  605. </span></code></pre>
  606. </div>
  607. <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>
  608. <p>There are several other features of GraphQL that we hope to make available to clients, such as:</p>
  609. <ul>
  610. <li>The ability to <em>batch requests</em>, where you can define dependencies between two separate queries and fetch data efficiently.</li>
  611. <li>The ability to <em>create subscriptions</em>, where your client can receive new data when it becomes available.</li>
  612. <li>The ability to <em>defer data</em>, where you choose to mark a part of your response as time-insensitive.</li>
  613. </ul>
  614. <h2 id="defining-the-schema">Defining the schema</h2>
  615. <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>
  616. <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>
  617. <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>
  618. <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>
  619. <h2 id="open-source">Open source</h2>
  620. <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>
  621. <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>
  622. <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>
  623. <h2 id="the-future">The future</h2>
  624. <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>
  625. <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>
  626. <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>
  627. <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>
  628. </article>
  629. </section>
  630. <nav id="jumpto">
  631. <p>
  632. <a href="/david/blog/">Accueil du blog</a> |
  633. <a href="http://githubengineering.com/the-github-graphql-api/">Source originale</a> |
  634. <a href="/david/stream/2019/">Accueil du flux</a>
  635. </p>
  636. </nav>
  637. <footer>
  638. <div>
  639. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  640. <p>
  641. Bonjour/Hi!
  642. Je suis <a href="/david/" title="Profil public">David&nbsp;Larlet</a>, je vis actuellement à Montréal et j’alimente cet espace depuis 15 ans. <br>
  643. Si tu as apprécié cette lecture, n’hésite pas à poursuivre ton exploration. Par exemple via les <a href="/david/blog/" title="Expériences bienveillantes">réflexions bimestrielles</a>, la <a href="/david/stream/2019/" title="Pensées (dés)articulées">veille hebdomadaire</a> ou en t’abonnant au <a href="/david/log/" title="S’abonner aux publications via RSS">flux RSS</a> (<a href="/david/blog/2019/flux-rss/" title="Tiens c’est quoi un flux RSS ?">so 2005</a>).
  644. </p>
  645. <p>
  646. Je m’intéresse à la place que je peux avoir dans ce monde. En tant qu’humain, en tant que membre d’une famille et en tant qu’associé d’une coopérative. De temps en temps, je fais aussi des <a href="https://github.com/davidbgk" title="Principalement sur Github mais aussi ailleurs">trucs techniques</a>. Et encore plus rarement, <a href="/david/talks/" title="En ce moment je laisse plutôt la place aux autres">j’en parle</a>.
  647. </p>
  648. <p>
  649. Voici quelques articles choisis :
  650. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  651. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  652. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  653. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  654. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  655. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  656. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  657. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  658. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  659. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  660. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  661. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  662. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  663. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  664. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  665. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  666. </p>
  667. <p>
  668. On peut <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">échanger par courriel</a>. Si éventuellement tu souhaites que l’on travaille ensemble, tu devrais commencer par consulter le <a href="http://larlet.com">profil dédié à mon activité professionnelle</a> et/ou contacter directement <a href="http://scopyleft.fr/">scopyleft</a>, la <abbr title="Société coopérative et participative">SCOP</abbr> dont je fais partie depuis six ans. Je recommande au préalable de lire <a href="/david/blog/2018/cout-site/" title="Attention ce qui va suivre peut vous choquer">combien coûte un site</a> et pourquoi je suis plutôt favorable à une <a href="/david/pro/devis/" title="Discutons-en !">non-demande de devis</a>.
  669. </p>
  670. <p>
  671. Je ne traque pas ta navigation mais mon
  672. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  673. conserve des logs d’accès.
  674. </p>
  675. </div>
  676. </footer>
  677. <script type="text/javascript">
  678. ;(_ => {
  679. const jumper = document.getElementById('jumper')
  680. jumper.addEventListener('click', e => {
  681. e.preventDefault()
  682. const anchor = e.target.getAttribute('href')
  683. const targetEl = document.getElementById(anchor.substring(1))
  684. targetEl.scrollIntoView({behavior: 'smooth'})
  685. })
  686. })()
  687. </script>