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
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
180
Optimizing APIs for Consumers with GraphQL
bswinnerton
2
460
Launching GitHub's Public GraphQL API
bswinnerton
2
580
GitHub GraphQL API
bswinnerton
4
160
GraphQL for Rubyists
bswinnerton
0
320
The Road To Code: Ruby
bswinnerton
0
120
The history of Vim
bswinnerton
0
160
Other Decks in Technology
See All in Technology
サイバー捜査員研修(前半)
nomizone
1
2k
DatadogのBits Chatが開発組織にもたらしたもの / What Bits Chat Has Brought Us
sms_tech
1
270
ハーレムエンジニアリング
kazuma777777
0
160
Master Dataグループ紹介資料
sansan33
PRO
1
4.8k
研究開発部の紹介 / Sansan R&D Profile
sansan33
PRO
4
24k
20分でわかるセキュアAPI
nwiizo
2
280
Digitization部 紹介資料
sansan33
PRO
2
7.7k
Bits AI を制するものは Datadog を制す / The player that controls Bits AI, controls Datadog
kaminashi
0
100
2026-08-05 IBM Z開発最前線!IBM Bob Premium Package for Zって何がすごいの?
yutanonaka
0
120
ラジオの科学
frievea
0
320
20260807_第6回_関東kaggler会LT_claw系bot xangiと始める、"寂しくない" kaggle
sugupoko
0
280
メルカリのグローバルアプリで挑んだ AlloyDB 運用と課題解決の実践記
hatappi
0
250
Featured
See All Featured
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
A Modern Web Designer's Workflow
chriscoyier
698
190k
Thoughts on Productivity
jonyablonski
76
5.3k
Practical Orchestrator
shlominoach
191
12k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
360
SEO for Brand Visibility & Recognition
aleyda
0
4.7k
Chasing Engaging Ingredients in Design
codingconduct
0
270
Why Our Code Smells
bkeepers
PRO
340
58k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
630
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
Groundhog Day: Seeking Process in Gaming for Health
codingconduct
0
280
Google's AI Overviews - The New Search
badams
0
1.1k
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