Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Launching GitHub's GraphQL API
Search
Brooks Swinnerton
October 11, 2017
Technology
530
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
190
Optimizing APIs for Consumers with GraphQL
bswinnerton
2
460
Launching GitHub's Public GraphQL API
bswinnerton
2
590
GitHub GraphQL API
bswinnerton
4
160
GraphQL for Rubyists
bswinnerton
0
330
The Road To Code: Ruby
bswinnerton
0
120
The history of Vim
bswinnerton
0
170
Other Decks in Technology
See All in Technology
2026/09/10 Spring Bootから Jakarta EE/MicroProfileへの移行
megascus
0
350
事業課題から技術的負債に向き合う
sansantech
PRO
2
610
山手線を徒歩で一周してわかった、 位置情報アプリは「足」が最強のデバッガー
hinakko
0
140
Oracle Cloud Infrastructure IaaS 新機能アップデート 2026/6 - 2026/8
oracle4engineer
PRO
0
200
20260912_スクフェス三河
kgnkhkr
0
340
Podは生きているのにGoだけが落ちる:GOGCとGOMEMLIMITで追うInvisible OOM Killの謎
tkc66buzz
1
690
おい、エージェントを使って終わらせろ
nwiizo
0
130
越境するなら専門用語を使うな高校校歌 / If you wanna cross border, you shouldn't use jargon
vtryo
0
130
WAF 運用改善の承認サイクル/SRE_BizReach_MIXI_1
visional_engineering_and_design
2
630
Minecraft JavaのMODをSwiftで作る
1mash0
0
140
GoCon2026 - Open Source, Open World
sanposhiho
4
3.5k
Slack上でインフラをトラブルシュートする! Agentic Platform Engineeringの第一歩
teru0x1
4
1.6k
Featured
See All Featured
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.6k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.9k
We Have a Design System, Now What?
morganepeng
55
8.3k
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
230
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
930
Information Architects: The Missing Link in Design Systems
soysaucechin
1
1.1k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.2k
Music & Morning Musume
bryan
47
7.4k
The untapped power of vector embeddings
frankvandijk
2
1.9k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
680
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
1k
Why You Should Never Use an ORM
jnunemaker
PRO
61
10k
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