Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Efficient Data Fetching with GraphQL and Relay

Efficient Data Fetching with GraphQL and Relay

Dan Schafer

July 22, 2015
Tweet

More Decks by Dan Schafer

Other Decks in Programming

Transcript

  1. Efficient Data Fetching with GraphQL and Relay Dan Schafer Facebook

    Product Infrastructure @dlschafer
  2. Flux

  3. Flux Dispatcher Action Store View Action

  4. Flux Dispatcher Action Store View Action Server

  5. Flux Dispatcher Action Store View Action Server

  6. None
  7. None
  8. <FriendList>

  9. <FriendList> <FriendListItem>

  10. <FriendList> <FriendListItem> <FriendInfo>

  11. Server <FriendList> <FriendListItem> <FriendInfo>

  12. Server <FriendList> <FriendListItem> <FriendInfo>

  13. Server <FriendList> <FriendListItem> <FriendInfo>

  14. Server <FriendList> <FriendListItem> <FriendInfo>

  15. Server <FriendList> <FriendListItem> <FriendInfo>

  16. Server <FriendList> <FriendListItem> <FriendInfo>

  17. Server <FriendList> <FriendListItem> <FriendInfo>

  18. Server <FriendList> <FriendListItem> <FriendInfo>

  19. Server <FriendList> <FriendListItem> <FriendInfo>

  20. Server <FriendList> <FriendListItem> <FriendInfo>

  21. Server <FriendList> <FriendListItem> <FriendInfo>

  22. Server <FriendList> <FriendListItem> <FriendInfo>

  23. Server <FriendList> <FriendListItem> <FriendInfo>

  24. Server <FriendList> <FriendListItem> <FriendInfo>

  25. Server <FriendList> <FriendListItem> <FriendInfo>

  26. Response Shape

  27. Composition

  28. Composition <FriendListItem> <FriendInfo>

  29. Composition <FriendListItem> <FriendInfo>

  30. Composition <FriendListItem> <FriendInfo>

  31. None
  32. None
  33. { id: 3500401,

  34. { id: 3500401, name: "Jing Chen",

  35. { id: 3500401, name: "Jing Chen", isViewerFriend: true,

  36. { id: 3500401, name: "Jing Chen", isViewerFriend: true, mutualFriends: {

    count: 195 },
  37. { id: 3500401, name: "Jing Chen", isViewerFriend: true, mutualFriends: {

    count: 195 }, profilePicture: { uri: "http://…", width: 50, height: 50 } }
  38. { id: 3500401, name: "Jing Chen", isViewerFriend: true, mutualFriends: {

    count: 195 }, profilePicture: { uri: "http://…", width: 50, height: 50 } }
  39. { id name isViewerFriend mutualFriends { count } profilePicture {

    uri width height } }
  40. { id name isViewerFriend mutualFriends { count } profilePicture {

    uri width height } }
  41. GraphQL

  42. GraphQL { { "id": "1572451031", "name": "Daniel Schafer" } }

  43. GraphQL { { "id": "1572451031", "name": "Daniel Schafer" } }

    { id name }
  44. GraphQL { "node": { "id": "1572451031", "name": "Daniel Schafer" }

    } { node(id: 1572451031) { id name } }
  45. GraphQL { "node": { "id": "1572451031", "name": "Daniel Schafer" }

    } { node(id: 1572451031) { id name birthdate { month day } } }
  46. GraphQL { "node": { "id": "1572451031", "name": "Daniel Schafer", "birthdate":

    { "month": 1, "day": 17, } } } { node(id: 1572451031) { id name birthdate { month day } } }
  47. GraphQL + React

  48. None
  49. None
  50. GraphQL + React

  51. 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
  52. 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
  53. 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
  54. 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
  55. 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
  56. 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
  57. 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
  58. Composition

  59. None
  60. None
  61. Composition

  62. Composition var FriendListItem = React.createClass({ render: function() { return (

    <div> <ProfilePic user={this.props.user} /> <FriendInfo user={this.props.user} /> </div> ); } });
  63. 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> ); } });
  64. 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> ); } });
  65. Lifecycle

  66. Lifecycle var FriendListItem = React.createClass({ statics: { queries: { user:

    function() { return graphql` User { ${ProfilePic.getQuery('user')} ${FriendInfo.getQuery('user')} } `; } } } }); Static View
  67. Lifecycle var FriendListItem = React.createClass({ statics: { queries: { user:

    function() { return graphql` User { ${ProfilePic.getQuery('user')} ${FriendInfo.getQuery('user')} } `; } } } }); Static View FriendListItem
  68. Lifecycle Query { ')} ')} Static View

  69. Lifecycle Query { ')} ')} Static View ProfilePic FriendInfo FriendListItem

  70. Lifecycle Query Server ew

  71. Lifecycle { node(id: 1572451031) { name mutualFriends { count },

    profilePicture { uri width height } } } Query Server ew
  72. Lifecycle y Server Data

  73. Lifecycle { "node": { name: "Daniel Schafer", mutualFriends: { count:

    195 }, profilePicture: { uri: "http://...", width: 50, height: 50 } } } y Server Data
  74. Lifecycle Server Data Store

  75. Lifecycle Server Data Store Flux

  76. Lifecycle a Store Views

  77. Lifecycle <div> <ProfilePic user={this.props.user} /> <FriendInfo user={this.props.user} /> </div> a

    Store Views
  78. Lifecycle Query Server Data Store Views Static View

  79. 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
  80. Lifecycle Query Server Data Store Views Static View

  81. Relay

  82. Pagination

  83. Pagination { "1572451031": { "name": "Daniel Schafer" } } {

    node(id: 1572451031) { name } }
  84. Pagination { "1572451031": { "name": "Daniel Schafer" } } {

    node(id: 1572451031) { name friends { name } } }
  85. Pagination { "1572451031": { "name": "Daniel Schafer", "friends": [ {

    "name": "Jing Chen" }, { "name": "Lee Byron" }, { "name": "Nick Schrock" }, { "name": "Joe Savona" } ] } } { node(id: 1572451031) { name friends { name } } }
  86. 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 } } }
  87. Pagination { "1572451031": { "name": "Daniel Schafer", "friends": [ {

    "name": "Jing Chen" }, { "name": "Lee Byron" } ] } } { node(id: 1572451031) { name friends(first: 2) { name } } }
  88. Pagination { "1572451031": { "name": "Daniel Schafer", "friends": [ {

    "name": "Jing Chen" }, { "name": "Lee Byron" } ] } } { node(id: 1572451031) { name friends(first: 2 offset: 2) { name } } }
  89. Offsets? Offset 0 Offset 1

  90. Offsets? Offset 0 Offset 1

  91. Offsets? Offset 0 Offset 1

  92. Offsets? Offset 1 Offset 2 Offset 0

  93. Offsets?

  94. None
  95. Cursors

  96. Cursors { "1572451031": { "name": "Daniel Schafer", "friends": [ {

    "name": "Jing Chen" }, { "name": "Lee Byron" } ] } } { node(id: 1572451031) { name friends(first: 2) { name } } }
  97. Cursors { "1572451031": { "name": "Daniel Schafer", "friends": [ {

    "name": "Jing Chen" }, { "name": "Lee Byron" } ] } } { node(id: 1572451031) { name friends(first: 2) { edges { node { name } cursor } } } }
  98. 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 } } } }
  99. 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 } } } }
  100. 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" } ] } } }
  101. 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" } ] } } }
  102. 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": [] } } }
  103. 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 } } } }
  104. 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" } ] } } }
  105. 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 }, } }
  106. 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 }, } }
  107. 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 }, } }
  108. Connections in Relay

  109. Connections in Relay var FriendList = React.createClass({ render: function() {

    return ( <div> { this.props.viewer.friends.map( function(user) { return <FriendListItem user={user} />; } ) } </div> ); } });
  110. Connections in Relay var FriendList = React.createClass({ statics: { queries:

    { viewer: function() { return graphql` Viewer { friends { ${FriendListItem.getQuery(‘user’)}, } } `; } } }, render: function() { ... } });
  111. Connections in Relay var FriendList = React.createClass({ statics: { queries:

    { viewer: function() { return graphql` Viewer { friends(first: 10) { ${FriendListItem.getQuery(‘user’)}, } } `; } } }, render: function() { ... } });
  112. Cursors

  113. None
  114. Declarative Pagination

  115. Connections in Relay var FriendList = React.createClass({ statics: { queries:

    { viewer: function() { return graphql` Viewer { friends(first: 10) { ${FriendListItem.getQuery(‘user’)}, } } `; } } }, render: function() { ... } });
  116. 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() { ... } });
  117. 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() { ... } });
  118. Mutations

  119. Subscriptions

  120. Relay August 2015

  121. GraphQL https://github.com/facebook/graphql https://github.com/graphql/graphql-­‐js

  122. Thanks! Dan Schafer @dlschafer