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
Efficient Data Fetching with GraphQL and Relay
Search
Dan Schafer
July 22, 2015
Programming
410
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Efficient Data Fetching with GraphQL and Relay
Presented at
http://www.meetup.com/ReactJS-San-Francisco/events/223945265/
Dan Schafer
July 22, 2015
More Decks by Dan Schafer
See All by Dan Schafer
The Prehistory of GraphQL
dschafer
1
410
GraphQL: Client-Driven Development
dschafer
0
360
GraphQL at Facebook
dschafer
0
210
GraphQL: A Horizontal Platform
dschafer
2
250
Other Decks in Programming
See All in Programming
Laravel Boostに学ぶ、AIにPHPを書かせる技術 〜OSSの実装から蒸留するエージェント制御の王道〜
kentaroutakeda
3
660
ビデオ通話が繋がる0.2秒で何が起きているのか
supurazako
2
170
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
520
作るコストが小さくなった時代 幸せに働くために改めて考えたいこと 〜エンジニアとして価値を出し続けるために注視している二分野〜
yuppeeng
0
180
20260722_microCMSで考える、AI時代のコンテンツ運用設計
yosh1
0
340
仕様書を書く前にハーネスを作る - Agent Native開発は「探索を速く、判定を固く」
gotalab555
4
1.5k
ここ半年くらいでAIに作らせたR用ツール
eitsupi
0
360
PHP Application における Kubernetes 内 gRPC 通信
ganchiku
0
580
Claude Opus 4.6以後の受託開発エンジニアの変化(Claude Code開発ノウハウ大公開スペシャルbyクラスメソッド)
iidatakuma
1
970
JAWS-UG横浜 #102 AWSサ終供養LT会 成仏できない AWS サービスたち 〜本日、三体供養します〜
maroon1st
0
310
継続モナドとリアクティブプログラミング
yukikurage
3
680
テーブルをDELETEした
yuzneri
0
130
Featured
See All Featured
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
250
Navigating Team Friction
lara
192
16k
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
380
Why Our Code Smells
bkeepers
PRO
340
58k
Darren the Foodie - Storyboard
khoart
PRO
3
3.5k
Optimizing for Happiness
mojombo
378
71k
Claude Code のすすめ
schroneko
67
230k
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
600
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
45k
Rails Girls Zürich Keynote
gr2m
96
14k
Chasing Engaging Ingredients in Design
codingconduct
0
260
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
Transcript
Efficient Data Fetching with GraphQL and Relay Dan Schafer Facebook
Product Infrastructure @dlschafer
Flux
Flux Dispatcher Action Store View Action
Flux Dispatcher Action Store View Action Server
Flux Dispatcher Action Store View Action Server
None
None
<FriendList>
<FriendList> <FriendListItem>
<FriendList> <FriendListItem> <FriendInfo>
Server <FriendList> <FriendListItem> <FriendInfo>
Server <FriendList> <FriendListItem> <FriendInfo>
Server <FriendList> <FriendListItem> <FriendInfo>
Server <FriendList> <FriendListItem> <FriendInfo>
Server <FriendList> <FriendListItem> <FriendInfo>
Server <FriendList> <FriendListItem> <FriendInfo>
Server <FriendList> <FriendListItem> <FriendInfo>
Server <FriendList> <FriendListItem> <FriendInfo>
Server <FriendList> <FriendListItem> <FriendInfo>
Server <FriendList> <FriendListItem> <FriendInfo>
Server <FriendList> <FriendListItem> <FriendInfo>
Server <FriendList> <FriendListItem> <FriendInfo>
Server <FriendList> <FriendListItem> <FriendInfo>
Server <FriendList> <FriendListItem> <FriendInfo>
Server <FriendList> <FriendListItem> <FriendInfo>
Response Shape
Composition
Composition <FriendListItem> <FriendInfo>
Composition <FriendListItem> <FriendInfo>
Composition <FriendListItem> <FriendInfo>
None
None
{ id: 3500401,
{ id: 3500401, name: "Jing Chen",
{ id: 3500401, name: "Jing Chen", isViewerFriend: true,
{ id: 3500401, name: "Jing Chen", isViewerFriend: true, mutualFriends: {
count: 195 },
{ id: 3500401, name: "Jing Chen", isViewerFriend: true, mutualFriends: {
count: 195 }, profilePicture: { uri: "http://…", width: 50, height: 50 } }
{ id: 3500401, name: "Jing Chen", isViewerFriend: true, mutualFriends: {
count: 195 }, profilePicture: { uri: "http://…", width: 50, height: 50 } }
{ id name isViewerFriend mutualFriends { count } profilePicture {
uri width height } }
{ id name isViewerFriend mutualFriends { count } profilePicture {
uri width height } }
GraphQL
GraphQL { { "id": "1572451031", "name": "Daniel Schafer" } }
GraphQL { { "id": "1572451031", "name": "Daniel Schafer" } }
{ id name }
GraphQL { "node": { "id": "1572451031", "name": "Daniel Schafer" }
} { node(id: 1572451031) { id name } }
GraphQL { "node": { "id": "1572451031", "name": "Daniel Schafer" }
} { node(id: 1572451031) { id name birthdate { month day } } }
GraphQL { "node": { "id": "1572451031", "name": "Daniel Schafer", "birthdate":
{ "month": 1, "day": 17, } } } { node(id: 1572451031) { id name birthdate { month day } } }
GraphQL + React
None
None
GraphQL + React
var FriendInfo = React.createClass({ render: function() { return ( <div>
<span>{this.props.user.name}</span> <span>{this.props.user.mutualFriends.count} mutual friends</span> </div> ); } }); GraphQL + React
var FriendInfo = React.createClass({ statics: { queries: { } }
render: function() { return ( <div> <span>{this.props.user.name}</span> <span>{this.props.user.mutualFriends.count} mutual friends</span> </div> ); } }); GraphQL + React
var FriendInfo = React.createClass({ statics: { queries: { user: function()
{ return graphql` User { name mutualFriends { count } } `; } } }, render: function() { return ( <div> <span>{this.props.user.name}</span> <span>{this.props.user.mutualFriends.count} mutual friends</span> </div> ); } }); GraphQL + React
var FriendInfo = React.createClass({ statics: { queries: { user: function()
{ return graphql` User { name mutualFriends { count } } `; } } }, render: function() { return ( <div> <span>{this.props.user.name}</span> <span>{this.props.user.mutualFriends.count} mutual friends</span> </div> ); } }); GraphQL + React
var FriendInfo = React.createClass({ statics: { queries: { user: function()
{ return graphql` User { name mutualFriends { count } } `; } } }, render: function() { return ( <div> <span>{this.props.user.name}</span> <span>{this.props.user.mutualFriends.count} mutual friends</span> </div> ); } }); GraphQL + React
var FriendInfo = React.createClass({ statics: { queries: { user: function()
{ return graphql` User { name mutualFriends { count } } `; } } }, render: function() { return ( <div> <span>{this.props.user.name}</span> <span>{this.props.user.mutualFriends.count} mutual friends</span> </div> ); } }); GraphQL + React
var FriendInfo = React.createClass({ statics: { queries: { user: function()
{ return graphql` User { name mutualFriends { count } } `; } } }, render: function() { return ( <div> <span>{this.props.user.name}</span> <span>{this.props.user.mutualFriends.count} mutual friends</span> </div> ); } }); GraphQL + React
Composition
None
None
Composition
Composition var FriendListItem = React.createClass({ render: function() { return (
<div> <ProfilePic user={this.props.user} /> <FriendInfo user={this.props.user} /> </div> ); } });
Composition var FriendListItem = React.createClass({ statics: { queries: { user:
function() { return graphql` User { ?? ?? } `; } } }, render: function() { return ( <div> <ProfilePic user={this.props.user} /> <FriendInfo user={this.props.user} /> </div> ); } });
Composition var FriendListItem = React.createClass({ statics: { queries: { user:
function() { return graphql` User { ${ProfilePic.getQuery(‘user’)}, ${FriendInfo.getQuery(‘user’)} } `; } } }, render: function() { return ( <div> <ProfilePic user={this.props.user} /> <FriendInfo user={this.props.user} /> </div> ); } });
Lifecycle
Lifecycle var FriendListItem = React.createClass({ statics: { queries: { user:
function() { return graphql` User { ${ProfilePic.getQuery('user')} ${FriendInfo.getQuery('user')} } `; } } } }); Static View
Lifecycle var FriendListItem = React.createClass({ statics: { queries: { user:
function() { return graphql` User { ${ProfilePic.getQuery('user')} ${FriendInfo.getQuery('user')} } `; } } } }); Static View FriendListItem
Lifecycle Query { ')} ')} Static View
Lifecycle Query { ')} ')} Static View ProfilePic FriendInfo FriendListItem
Lifecycle Query Server ew
Lifecycle { node(id: 1572451031) { name mutualFriends { count },
profilePicture { uri width height } } } Query Server ew
Lifecycle y Server Data
Lifecycle { "node": { name: "Daniel Schafer", mutualFriends: { count:
195 }, profilePicture: { uri: "http://...", width: 50, height: 50 } } } y Server Data
Lifecycle Server Data Store
Lifecycle Server Data Store Flux
Lifecycle a Store Views
Lifecycle <div> <ProfilePic user={this.props.user} /> <FriendInfo user={this.props.user} /> </div> a
Store Views
Lifecycle Query Server Data Store Views Static View
var FriendInfo = React.createClass({ statics: { queries: { user: function()
{ return graphql` User { name mutualFriends { count } } `; } } }, render: function() { return ( <div> <span>{this.props.user.name}</span> <span>{this.props.user.mutualFriends.count} mutual friends</span> </div> ); } }); Response Shape
Lifecycle Query Server Data Store Views Static View
Relay
Pagination
Pagination { "1572451031": { "name": "Daniel Schafer" } } {
node(id: 1572451031) { name } }
Pagination { "1572451031": { "name": "Daniel Schafer" } } {
node(id: 1572451031) { name friends { name } } }
Pagination { "1572451031": { "name": "Daniel Schafer", "friends": [ {
"name": "Jing Chen" }, { "name": "Lee Byron" }, { "name": "Nick Schrock" }, { "name": "Joe Savona" } ] } } { node(id: 1572451031) { name friends { name } } }
Pagination { "1572451031": { "name": "Daniel Schafer", "friends": [ {
"name": "Jing Chen" }, { "name": "Lee Byron" }, { "name": "Nick Schrock" }, { "name": "Joe Savona" } ] } } { node(id: 1572451031) { name friends(first: 2) { name } } }
Pagination { "1572451031": { "name": "Daniel Schafer", "friends": [ {
"name": "Jing Chen" }, { "name": "Lee Byron" } ] } } { node(id: 1572451031) { name friends(first: 2) { name } } }
Pagination { "1572451031": { "name": "Daniel Schafer", "friends": [ {
"name": "Jing Chen" }, { "name": "Lee Byron" } ] } } { node(id: 1572451031) { name friends(first: 2 offset: 2) { name } } }
Offsets? Offset 0 Offset 1
Offsets? Offset 0 Offset 1
Offsets? Offset 0 Offset 1
Offsets? Offset 1 Offset 2 Offset 0
Offsets?
None
Cursors
Cursors { "1572451031": { "name": "Daniel Schafer", "friends": [ {
"name": "Jing Chen" }, { "name": "Lee Byron" } ] } } { node(id: 1572451031) { name friends(first: 2) { name } } }
Cursors { "1572451031": { "name": "Daniel Schafer", "friends": [ {
"name": "Jing Chen" }, { "name": "Lee Byron" } ] } } { node(id: 1572451031) { name friends(first: 2) { edges { node { name } cursor } } } }
Cursors { "1572451031": { "name": "Daniel Schafer", "friends": { "edges":
[ { "node": { "name": "Jing Chen" }, "cursor": "3500401" }, { "node": { "name": "Lee Byron" }, "cursor": "4802170" } ] } } } { node(id: 1572451031) { name friends(first: 2) { edges { node { name } cursor } } } }
Cursors { "1572451031": { "name": "Daniel Schafer", "friends": { "edges":
[ { "node": { "name": "Jing Chen" }, "cursor": "3500401" }, { "node": { "name": "Lee Byron" }, "cursor": "4802170" } ] } } } { node(id: 1572451031) { name friends(first: 2 after: 4802170) { edges { node { name } cursor } } } }
Cursors { "1572451031": { "name": "Daniel Schafer", "friends": { "edges":
[ { "node": { "name": "Jing Chen" }, "cursor": "3500401" }, { "node": { "name": "Lee Byron" }, "cursor": "4802170" } ] } } } { node(id: 1572451031) { name friends(first: 2 after: 4802170) { edges { node { name } cursor } } } } { "1572451031": { "name": "Daniel Schafer", "friends": { "edges": [ { "node": { "name": "Nick Schrock" }, "cursor": "37000641" }, { "node": { "name": "Joe Savona" }, "cursor": "842472" } ] } } }
Cursors { "1572451031": { "name": "Daniel Schafer", "friends": { "edges":
[ { "node": { "name": "Jing Chen" }, "cursor": "3500401" }, { "node": { "name": "Lee Byron" }, "cursor": "4802170" } ] } } } { node(id: 1572451031) { name friends(first: 2 after: 842472) { edges { node { name } cursor } } } } { "1572451031": { "name": "Daniel Schafer", "friends": { "edges": [ { "node": { "name": "Nick Schrock" }, "cursor": "37000641" }, { "node": { "name": "Joe Savona" }, "cursor": "842472" } ] } } }
Cursors { "1572451031": { "name": "Daniel Schafer", "friends": { "edges":
[ { "node": { "name": "Jing Chen" }, "cursor": "3500401" }, { "node": { "name": "Lee Byron" }, "cursor": "4802170" } ] } } } { node(id: 1572451031) { name friends(first: 2 after: 842472) { edges { node { name } cursor } } } } { "1572451031": { "name": "Daniel Schafer", "friends": { "edges": [] } } }
Cursors { "1572451031": { "name": "Daniel Schafer", "friends": { "edges":
[ { "node": { "name": "Jing Chen" }, "cursor": "3500401" }, { "node": { "name": "Lee Byron" }, "cursor": "4802170" } ] } } } { node(id: 1572451031) { name friends(first: 2) { edges { node { name } cursor } } } }
Cursors { "1572451031": { "name": "Daniel Schafer", "friends": { "edges":
[ { "node": { "name": "Jing Chen" }, "cursor": "3500401" }, { "node": { "name": "Lee Byron" }, "cursor": "4802170" } ] } } } { node(id: 1572451031) { name friends(first: 2) { edges { node { name } cursor } pageInfo { hasNextPage } } } } { "1572451031": { "name": "Daniel Schafer", "friends": { "edges": [ { "node": { "name": "Jing Chen" }, "cursor": "3500401" }, { "node": { "name": "Lee Byron" }, "cursor": "4802170" } ] } } }
Cursors { "1572451031": { "name": "Daniel Schafer", "friends": { "edges":
[ { "node": { "name": "Jing Chen" }, "cursor": "3500401" }, { "node": { "name": "Lee Byron" }, "cursor": "4802170" } ] } } } { node(id: 1572451031) { name friends(first: 2) { edges { node { name } cursor } pageInfo { hasNextPage } } } } { "1572451031": { "name": "Daniel Schafer", "friends": { "edges": [ { "node": { "name": "Jing Chen" }, "cursor": "3500401" }, { "node": { "name": "Lee Byron" }, "cursor": "4802170" } ], "pageInfo": { "hasNextPage": true }, } }
Cursors { "1572451031": { "name": "Daniel Schafer", "friends": { "edges":
[ { "node": { "name": "Jing Chen" }, "cursor": "3500401" }, { "node": { "name": "Lee Byron" }, "cursor": "4802170" } ] } } } { node(id: 1572451031) { name friends(first: 2 after: 4802170) { edges { node { name } cursor } pageInfo { hasNextPage } } } } { "1572451031": { "name": "Daniel Schafer", "friends": { "edges": [ { "node": { "name": "Jing Chen" }, "cursor": "3500401" }, { "node": { "name": "Lee Byron" }, "cursor": "4802170" } ], "pageInfo": { "hasNextPage": true }, } }
Cursors { "1572451031": { "name": "Daniel Schafer", "friends": { "edges":
[ { "node": { "name": "Jing Chen" }, "cursor": "3500401" }, { "node": { "name": "Lee Byron" }, "cursor": "4802170" } ] } } } { node(id: 1572451031) { name friends(first: 2 after: 4802170) { edges { node { name } cursor } pageInfo { hasNextPage } } } } { "1572451031": { "name": "Daniel Schafer", "friends": { "edges": [ { "node": { "name": "Nick Schrock" }, "cursor": "37000641" }, { "node": { "name": "Joe Savona" }, "cursor": "842472" } ], "pageInfo": { "hasNextPage": false }, } }
Connections in Relay
Connections in Relay var FriendList = React.createClass({ render: function() {
return ( <div> { this.props.viewer.friends.map( function(user) { return <FriendListItem user={user} />; } ) } </div> ); } });
Connections in Relay var FriendList = React.createClass({ statics: { queries:
{ viewer: function() { return graphql` Viewer { friends { ${FriendListItem.getQuery(‘user’)}, } } `; } } }, render: function() { ... } });
Connections in Relay var FriendList = React.createClass({ statics: { queries:
{ viewer: function() { return graphql` Viewer { friends(first: 10) { ${FriendListItem.getQuery(‘user’)}, } } `; } } }, render: function() { ... } });
Cursors
None
Declarative Pagination
Connections in Relay var FriendList = React.createClass({ statics: { queries:
{ viewer: function() { return graphql` Viewer { friends(first: 10) { ${FriendListItem.getQuery(‘user’)}, } } `; } } }, render: function() { ... } });
Connections in Relay var FriendList = React.createClass({ statics: { queryParams:
{count: 10}, queries: { viewer: function(params) { return graphql` Viewer { friends(first: ${params.count}) { ${FriendListItem.getQuery(‘user’)}, } } `; } } }, render: function() { ... } });
Connections in Relay var FriendList = React.createClass({ statics: { queryParams:
{count: 10}, queries: { viewer: function(params) { return graphql` Viewer { friends(first: ${params.count}) { ${FriendListItem.getQuery(‘user’)}, } } `; } } }, onScrollLoad: function() { this.setQueryParams({count: this.queryParams.count + 5}); }, render: function() { ... } });
Mutations
Subscriptions
Relay August 2015
GraphQL https://github.com/facebook/graphql https://github.com/graphql/graphql-‐js
Thanks! Dan Schafer @dlschafer