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
220
GraphQL: A Horizontal Platform
dschafer
2
260
Other Decks in Programming
See All in Programming
AIと壁打ちしながら進めるコスト管理
fufuhu
2
1.7k
初めての模倣学習とVLA
natsutan
0
250
170k Jobs a Day on GKE: Scaling Mercari's CI Platform - and What's Next for AI-Native Development
junyaokabe
0
120
わからない話を追いかけたら、プログラミング言語を作る側にいた
ydah
3
550
Claude Code全社展開のためにやったことn選~プラグイン302個・コミッター271人を支えるために~
kenchan
5
1.8k
GDG Korea Android: 2026 I/O Extended ~ What's new in Android development tools
pluu
0
240
AI時代に学ぶ 好きなルール 嫌いなルール Linter編
shorty5121
0
150
Introducing Stack Pull Request in GitHub
kkamegawa
0
100
Go 1.27 における memory allocation の高速化
andpad
0
320
自動化したのに回らない テスト運用の壁―AI時代の品質責任と生産性
mfunaki
0
510
異なる設計思想のフレームワークを経験して得た学び
amekuhideki
1
570
Hono + Inertia + React で LP を構築した話
oukayuka
2
170
Featured
See All Featured
How to Ace a Technical Interview
jacobian
281
24k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
650
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.4k
Facilitating Awesome Meetings
lara
57
7.1k
The #1 spot is gone: here's how to win anyway
tamaranovitovic
3
1.1k
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.9k
sira's awesome portfolio website redesign presentation
elsirapls
0
340
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.4k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.8k
Done Done
chrislema
186
16k
Self-Hosted WebAssembly Runtime for Runtime-Neutral Checkpoint/Restore in Edge–Cloud Continuum
chikuwait
0
740
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