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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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-11]SREは誰のもの?運用エンジニアが始める 「SRE領域への越境」とチームの進化の軌跡 〜Road to NEXT CRE
tosite
0
160
AIエージェントの自己改善をどう設計するか / How to Design Self-Improvement for AI Agents
22mi
3
2k
OpenTelemetry eBPF Instrumentationの舞台裏 / Behind the Scenes of OpenTelemetry eBPF Instrumentation
ymotongpoo
4
1.1k
Microsoft 365 Copilot chat -tekoälypalvelun tietosuojaongelmat
hponka
0
740
Gitは怖い?共有ワークスペースから始めるSnowflakeチーム開発
coco_se
0
180
Claude Code本って、 読む必要あるの?
oikon48
2
420
20260912_スクフェス三河
kgnkhkr
0
300
OpenTelemetryのメトリクスをCloudWatchに送ってPromQLで見てみた
ota1022
0
120
Sigmaで作る業務アプリ
kazushiro_honma
0
120
『止めない』を設計する — 制約の中で、事業の根幹を支える判断
hiroyaterui
0
270
enechainの内製セルフサービスプラットフォーム
hiyosi
0
160
AI時代のAPI品質を支えるガードレール / API Guardrails for API quality in the AI era
yokawasa
1
220
Featured
See All Featured
Everyday Curiosity
cassininazir
0
310
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
720
Noah Learner - AI + Me: how we built a GSC Bulk Export data pipeline
techseoconnect
PRO
0
430
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
530
Building Flexible Design Systems
yeseniaperezcruz
330
41k
Test your architecture with Archunit
thirion
2
2.4k
How to Think Like a Performance Engineer
csswizardry
28
2.8k
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
490
Building Adaptive Systems
keathley
44
3.2k
Chasing Engaging Ingredients in Design
codingconduct
0
310
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
46k
Optimising Largest Contentful Paint
csswizardry
37
3.9k
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