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
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
420
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
PHPプロジェクトの結合バランスを可視化する #php_night
kajitack
0
240
A2UI for Android: Safely Rendering AI-Generated UI with Jetpack Compose - DroidKaigi 2026
itsmedreamwalker
0
110
AI駆動開発にグラフDBを重ねてみた
satoshi256kbyte
2
790
AIを上手に使っていこうとしたら越境せざるを得なくなった話 〜実践1年で見えた境界を越えなければならない理由と進め方〜 / Crossing borders with AI
tomoyakitaura
4
1.1k
Go のフラットな パッケージに 「ファイル単位の private」を 〜Linter “declscope” を作った話〜
mpyw
0
130
AI Agent時代のリアーキテクチャ戦略と実践
hokaccha
9
4.2k
Deep dive into the select statement (GopherCon UK)
jespino
0
180
アクセシビリティから考える情報設計
high_g_engineer
0
370
自分的「カンファレンスの楽しみ方」
syumai
0
200
Webの地図
yosuke_furukawa
PRO
6
4.3k
市販E-Readerを乗っ取れ 〜Embedded Swiftで電子ペーパーガジェットを制御する〜
trickart
0
180
信頼性の目標を誰も求めてない
shubox
0
510
Featured
See All Featured
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
480
Crafting Experiences
bethany
1
330
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.6k
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
3k
AI: The stuff that nobody shows you
jnunemaker
PRO
9
990
How to Think Like a Performance Engineer
csswizardry
28
2.8k
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
850
The Cost Of JavaScript in 2023
addyosmani
55
10k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
330
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
700
The Impact of AI in SEO - AI Overviews June 2024 Edition
aleyda
6
1.2k
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