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
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
160
Other Decks in Technology
See All in Technology
[RSJ26] Hierarchy-Aware Multimodal Retrieval-Augmented Generation for Embodied Question Answering
keio_smilab
PRO
1
150
Webとヘルスデータ
yukukotani
0
120
Introduction to Sansan Meishi Maker Development Engineer
sansan33
PRO
0
470
Continuous Delivery! It is not what you think it is
tdpauw
0
160
書籍『生成AIの安全性入門』の入門
wataoka
0
150
研究開発部の紹介 / Sansan R&D Profile
sansan33
PRO
4
25k
Bet AI Day 2026丨AIを「使う」から、AIが「働く」へ ― LayerXが進める「組織AI」の社会実装
layerx
PRO
2
1.5k
データエンジニアの困りごとをDevinと一緒に解消する
10xinc
2
370
AIで実装は速くなった。なのにプロダクトは速くならない。職能の壁を越えて価値のフローを設計する
nwiizo
1
2.4k
IDperturb: Enhancing Variation in Synthetic Face Generation via Angular Perturbation
sansantech
PRO
0
100
「重なり」は迎える側がつくる ― 人もAIエージェントも歓迎するプロダクトエンジニアリング ―
go0517go
PRO
0
130
Data Hubグループ 紹介資料
sansan33
PRO
0
3.2k
Featured
See All Featured
YesSQL, Process and Tooling at Scale
rocio
174
15k
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.6k
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
330
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
Prompt Engineering for Job Search
mfonobong
0
430
Ruling the World: When Life Gets Gamed
codingconduct
0
320
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
The Curious Case for Waylosing
cassininazir
1
490
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
4.3k
How to train your dragon (web standard)
notwaldorf
97
6.8k
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
370
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
410
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