Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Launching GitHub's GraphQL API
Search
Brooks Swinnerton
October 11, 2017
Technology
520
4
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Launching GitHub's GraphQL API
Brooks Swinnerton
October 11, 2017
More Decks by Brooks Swinnerton
See All by Brooks Swinnerton
Building GitHub Integrations with Webhooks and REST
bswinnerton
1
170
Optimizing APIs for Consumers with GraphQL
bswinnerton
2
450
Launching GitHub's Public GraphQL API
bswinnerton
2
570
GitHub GraphQL API
bswinnerton
4
150
GraphQL for Rubyists
bswinnerton
0
310
The Road To Code: Ruby
bswinnerton
0
110
The history of Vim
bswinnerton
0
150
Other Decks in Technology
See All in Technology
Mastering Ruby Box
tagomoris
3
150
さきさん文庫の書籍ができるまで
sakiengineer
0
370
イベントストーミングとKiroの仕様駆動開発で実現する要件の認識合わせプロセス
syobochim
7
1.2k
関西に縁あるMicrosoft MVPsが語るCopilotの未来
kasada
0
1.2k
AI-DLCを活用した高品質・安全なAI駆動開発実践 / AI Driven Development
yoshidashingo
1
370
PHP と TypeScript の型システム比較:AI 時代の「型」は誰のためにあるのか? #frontend_phpcon_do / frontend_phpcon_do_2026
shogogg
1
250
タクシーアプリ『GO』の実践的データ活用
mot_techtalk
3
150
Djangoユーザが知っ得なPostgreSQL機能 - 設計の選択肢を増やす / Djang-use-PostgreSQL
soudai
PRO
0
190
新規事業を牽引する技術選定 〜フルスタックTypeScript開発の実践事例〜
nullnull
3
350
Dynamic Workersについて
yusukebe
2
590
形式手法特論:公平性制約の位相的特徴づけ #kernelvm / Kernel VM Study Kansai 12th
ytaka23
1
760
個人の発見を、組織の知恵に 〜生成AI活用を"探索"から"組織の仕組み"へ〜
kintotechdev
2
1k
Featured
See All Featured
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
2k
Context Engineering - Making Every Token Count
addyosmani
9
940
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
320
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
The innovator’s Mindset - Leading Through an Era of Exponential Change - McGill University 2025
jdejongh
PRO
1
190
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
830
Building AI with AI
inesmontani
PRO
1
1.1k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
2.9k
The Curse of the Amulet
leimatthew05
1
13k
The AI Revolution Will Not Be Monopolized: How open-source beats economies of scale, even for LLMs
inesmontani
PRO
3
3.5k
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
200
Future Trends and Review - Lecture 12 - Web Technologies (1019888BNR)
signer
PRO
0
3.6k
Transcript
GraphQL
Hi, I’m Brooks
I work at !
Launching GitHub’s GraphQL API
March 2008 API v1 April 2009 API v2 April 2011
API v3 May 2017 API v4
March 2008 API v1 April 2009 API v2 April 2011
API v3 May 2017 API v4
March 2008 API v1 April 2009 API v2 April 2011
API v3 May 2017 API v4
March 2008 API v1 April 2009 API v2 April 2011
API v3 May 2017 API v4
https://api.github.com/user GET
!"""# verb https://api.github.com/user GET
!"""""""""""""""""""""""""""# endpoint !"""# verb https://api.github.com/user GET
{ "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": "
[email protected]
", "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
REST APIs return resources
{ "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": "
[email protected]
", "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
{ "login": "bswinnerton", "id": 934497, "name": "Brooks Swinnerton", "location": "Brooklyn,
NY", "email": "
[email protected]
", "bio": ":octocat:", ... } https://api.github.com/user GET
https://api.github.com/user GET { "login": "bswinnerton", "id": 934497, "name": "Brooks Swinnerton",
"location": "Brooklyn, NY", "email": "
[email protected]
", "bio": ":octocat:", "public_repos": 32, "public_gists": 55, "private_gists": 172, "owned_private_repos": 8, ... }
but how do we access those other resources?
✨ hypermedia ✨
https://api.github.com/user GET { "login": "bswinnerton", "id": 934497, "name": "Brooks Swinnerton",
"location": "Brooklyn, NY", "email": "
[email protected]
", "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", ... }
https://api.github.com/user GET { "login": "bswinnerton", "id": 934497, "name": "Brooks Swinnerton",
"location": "Brooklyn, NY", "email": "
[email protected]
", "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", ... }
https://api.github.com/users/bswinnerton/repos GET
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}" }, ... ]
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}" }, ... ]
https://api.github.com/repos/bswinnerton/launchbar-github/issues GET
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", ... }, ... ]
API Server /user /repositories /issues
API Server /user /repositories /issues
"RESTful APIs are optimized for servers, not clients." - Mark
Twain (I think)
how can we put API consumers first?
March 2008 API v1 April 2009 API v2 April 2011
API v3 Early 2016 ?
Enter GraphQL
a data query language
think SQL
not Neo4j
GraphQL is a specification
{ viewer { name email } }
{ viewer { name email } }
{ viewer { name email } }
{ viewer { name email } }
{ viewer { name email } } { "data": {
"viewer": { "name": "Brooks Swinnerton", "email": "
[email protected]
" } } }
https://api.github.com/graphql POST
!"""# verb https://api.github.com/graphql POST
!"""# verb !""""""""""""""""""""""""""""""# endpoint https://api.github.com/graphql POST
https://api.github.com/graphql POST { user(login:"defunkt") { name bio } } {
"data": { "user": { "name": "Chris Wanstrath", "bio": """ } } }
features of the query language
GraphQL is typed
{ viewer { name email } }
{ viewer { name email } } type RootQuery {
viewer: User } type User { name: String email: String }
{ viewer { name email } } type RootQuery {
viewer: User } type User { name: String email: String }
{ viewer { name email } } type RootQuery {
viewer: User } type User { name: String email: String }
{ user(login:"defunkt") { name bio } }
type RootQuery { user(login: String): User } type User {
name: String bio: String } { user(login:"defunkt") { name bio } }
{ user(login:"defunkt") { name bio } } type RootQuery {
user(login: String): User } type User { name: String bio: String }
type RootQuery { user(login: String): User } type User {
name: String bio: String } { user(login:"defunkt") { name bio } }
type RootQuery { user(login: String): User } type User {
name: String bio: String } { user(login:"defunkt") { name bio } }
{ licenses { name nickname url } }
{ licenses { name nickname url } }
{ licenses { name nickname url } } type RootQuery
{ licenses: [License]! } type License { name: String! nickname: String url: URL! }
{ licenses { name nickname url } } type RootQuery
{ licenses: [License]! } type License { name: String! nickname: String url: URL! }
{ licenses { name nickname url } } type RootQuery
{ licenses: [License]! } type License { name: String! nickname: String url: URL! }
aliases
{ user(login: "defunkt") { name bio } user(login: "bswinnerton") {
name bio } }
{ user(login: "defunkt") { name bio } user(login: "bswinnerton") {
name bio } } { "data": { "user": { "name": "Chris Wanstrath", "bio": """ }, "user": { "name": "Brooks Swinnerton", "bio": "#$" } } }
{ user(login: "defunkt") { name bio } user(login: "bswinnerton") {
name bio } } { "data": { "user": { "name": "Chris Wanstrath", "bio": """ }, "user": { "name": "Brooks Swinnerton", "bio": "#$" } } }
{ chris: user(login: "defunkt") { name bio } brooks: user(login:
"bswinnerton") { name bio } }
{ "data": { "chris": { "name": "Chris Wanstrath", "bio": """
}, "brooks": { "name": "Brooks Swinnerton", "bio": "#$" } } }
{ "data": { "chris": { "name": "Chris Wanstrath", "bio": """
}, "brooks": { "name": "Brooks Swinnerton", "bio": "#$" } } }
fragments
{ chris: user(login: "defunkt") { name bio } brooks: user(login:
"bswinnerton") { name bio } }
{ chris: user(login: "defunkt") { ...UserInfo } brooks: user(login: "bswinnerton")
{ ...UserInfo } } fragment UserInfo on User { name bio }
variables
{ user(login:"defunkt") { name bio } }
query { user(login:"defunkt") { name bio } }
query($login:String!) { user(login:$login) { name bio } }
{ "login": "defunkt" } query($login:String!) { user(login:$login) { name bio
} }
{ "data": { "user": { "name": "Chris Wanstrath", "bio": """
} } } { "login": "defunkt" } query($login:String!) { user(login:$login) { name bio } }
mutations
mutation { createProject(input:{ownerId:"1234",name:"to do"}) { project { url } }
}
mutation { createProject(input:{ownerId:"1234",name:"to do"}) { project { url } }
}
mutation { createProject(input:{ownerId:"1234",name:"to do"}) { project { url } }
}
mutation { createProject(input:{ownerId:"1234",name:"to do"}) { project { url } }
}
{ "data": { "createProject": { "project": { "url": "https://github.com/rails/rails/projects/1" }
} } }
Relay
{ viewer { repositories { totalCount } } }
{ viewer { repositories { totalCount } } } {
"data": { "viewer": { "repositories": { "totalCount": 65 } } } }
{ viewer { repositories(first:2) { edges { node { name
} } } } }
{ viewer { repositories(first:2) { edges { node { name
} } } } }
viewer dotfiles failed startup code resume blog million dollar idea
code
User Repository Repository Repository Repository Repository Edges
User Repository Repository Repository Repository Repository Nodes
{ viewer { repositories(first:2) { edges { node { name
} } } } }
{ viewer { repositories(first:2) { edges { node { name
} } } } }
{ viewer { repositories(first:2) { edges { node { name
} } } } } { "data": { "viewer": { "repositories": { "edges": [ { "node": { "name": "nyc-restaurant-grades" } }, { "node": { "name": "launchbar-github" } } ] } } } }
{ 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" } } ] } } } }
{ 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" } } ] } } } }
{ 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" } } ] } } } }
{ viewer { repositories(first:2, after:"Y3Vyc29yOnYyOpHOBOlMSg==") { edges { node {
name } } } } }
{ "data": { "viewer": { "repositories": { "edges": [ {
"cursor": "Y3Vyc29yOnYyOpHOAIibww==", "node": { "name": "bswinnerton.github.io" } }, { "cursor": "Y3Vyc29yOnYyOpHOArDTpw==", "node": { "name": "dotfiles" } } ] } } } }
GraphQL is introspectable
documentation and client generation, are free
https://developer.github.com/v4/explorer/
https://developer.github.com/v4/explorer/
https://developer.github.com/v4/explorer/
https://developer.github.com/v4/explorer/
https://developer.github.com/v4/explorer/
https://developer.github.com/v4/explorer/
https://developer.github.com/v4/explorer/
https://developer.github.com/v4/explorer/
https://developer.github.com/v4/explorer/
https://developer.github.com/v4/explorer/
https://developer.github.com/v4/explorer/
https://developer.github.com/v4/explorer/
https://developer.github.com/v4/explorer/
https://developer.github.com/v4/explorer/
https://developer.github.com/v4/explorer/
https://developer.github.com/v4/explorer/
multiple resources in one round trip
API Server /user /repositories /issues
API Server /graphql
how can we put API consumers first?
March 2008 API v1 April 2009 API v2 April 2011
API v3 Early 2016 ?
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
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
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
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
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
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
Rate Limiting
REST API: 5,000 req/hour
GraphQL: ?
GraphQL: 500,000 node count
GraphQL: 5,000 point score
{ viewer { repositories(last:100) { edges { node { name
issues(last:50) { edges { node { title } } } } } } } rateLimit(dryRun:true) { nodeCount } }
{ viewer { repositories(last:100) { edges { node { name
issues(last:50) { edges { node { title } } } } } } } rateLimit(dryRun:true) { nodeCount } }
{ viewer { repositories(last:100) { edges { node { name
issues(last:50) { edges { node { title } } } } } } } rateLimit(dryRun:true) { nodeCount } } { "data": { "rateLimit": { "nodeCount": 5100 } } }
{ viewer { repositories(last:100) { edges { node { name
issues(last:50) { edges { node { title } } } } } } } rateLimit(dryRun:true) { cost } } { "data": { "rateLimit": { "cost": 1 } } }
Schema Driven Development
UI Development Staff Ship Production REST API
but today…
all new features are built with GraphQL
from the start
GraphQL Staff Ship Production REST API UI Development
this allows us to build a true public API
this allows us to build a true public API
this allows us to build a true platform
github/graphql-client
exchange a query for Ruby objects
collocate our queries in our views
@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
GraphQL-backed REST APIs
Staff Ship Production GraphQL UI Development REST API
github/scientist
https://api.github.com/user Legacy REST New GraphQL Return result Compare return values
ensures data accuracy
provides speed comparisons
gjtorikian/graphql-docs
https://github.com/gjtorikian/graphql-docs
https://github.com/gjtorikian/graphql-docs
automated changelog
https://developer.github.com/v4/changelog
community site
None
What’s next for the GraphQL API
confidence in schema coverage
GitHub Apps integration
More information
https://developer.github.com
http://graphql.org
http://platform.github.community
thank you @bswinnerton