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

GraphQL and Relay

492a25871b1279286bdb95c7bacb7697?s=47 Helielson
January 29, 2016

GraphQL and Relay

Concepts, Spec and how we are using it at jusbrasil.com.br

492a25871b1279286bdb95c7bacb7697?s=128

Helielson

January 29, 2016
Tweet

Transcript

  1. GraphQL and Relay

  2. @helielson @hyetho @rodrigopr @rodrigoropr

  3. Topics - GraphQL What is it? Motivation Spec Implementations How

    we use it What is coming
  4. Definition GraphQL is a query language designed to build client

    applications by providing an intuitive and flexible syntax for describing their data requirements and interactions.
  5. Why invent something new? What about REST and Ad hoc

    endpoints?
  6. REST problems - Multiple round trips to render single views

    - Receives unnecessary data - Payloads tend to grow over time for all clients
  7. - Code duplication - API changes with client need -

    Hard to scale with multiple clients - Hard to keep backwards compatibility Ad hoc endpoints (view oriented)
  8. How we think about data? - Not tables, joins or

    URIs - But: - Objects - Properties - Relationships - What, not How
  9. GraphQL

  10. - Product-centric - Hierarchical - Client-specified queries - Strongly-typed Principles:

  11. Product-centric

  12. Hierarchical

  13. { post(id: 65) { title, votes, author { name, avatar

    } } } Hierarchical
  14. { post(id: 65) { title, votes, author { name, avatar

    } } } Hierarchical { "post": { "title": "Post title", "votes": 100, "author" { "name": "Didi", "avatar": "http://x.jpg" } } }
  15. Strong-Typing

  16. Client-specific Queries

  17. Not tied to: - Language - Framework - Database -

    Protocol
  18. Schema - Think as a Graph - Define structure of

    nodes (properties) - How they are connected
  19. Schema User { name: String! email: String! followers: [User] isFriendOf(otherEmail:

    String!): Boolean }
  20. Schema query { post(postId: Int!): Post! user(email: String!): User! me:

    User }
  21. Type System - Interface - Object - Enum - Union

    - List - Scalars: - String, Int, Boolean
  22. Query language { user(email: "toyota@jusbrasil.com.br") { name followers { name

    } } }
  23. query { user(email: "toyota@jusbrasil.com.br") { ...userInfo followers { ...userInfo }

    } } fragment userInfo on User { name, email lastPosts { ...somePostFrament } }
  24. fragment petInfo on Pet { petName, owner { name }

    … on Dog { noiseVolume: barkVolume } … on Cat { noiseVolume: meowVolume } }
  25. Variables query houseTrainedQuery($atOtherHomes: Boolean) { dog { isHousetrained(atOtherHomes: $atOtherHomes) }

    }
  26. Fragment directives fragment maybeFragment on Query @include(if: $condition) { me

    { name } }
  27. Fragment directives query myQuery($someTest: Boolean) { experimentalField @skip(if: $someTest) }

  28. Mutation mutation { submitComment(postId: 1, user: "...", content: "") {

    createdComment { id, url } } }
  29. Open to optimizations - Batch - Cache - "DataLoader is

    the true" http://github.com/facebook/dataloader
  30. Network-GraphQL (how we are using) - Unify object definition -

    Unify loader definition - Helper for thrift-style relationships - Add an extra optimization layer - Auto extract selections from query
  31. Network-GraphQL (how we are using) - Define the schema based

    on jusbrasil-types - Rethinking data structure - english - relationships - building as we need - Based on network Thrift API
  32. What's coming next? - Open source movement - Improve data

    coverage - Provide authorization in schema (acl) - Mutation (done) - Object authorization - Field authorization
  33. Questions?

  34. Relay

  35. Topics - Relay What is it? Motivation Spec Implementation How

    we use it What is coming
  36. Definition A javascript framework for building data-driven react applications

  37. Dispatcher View Store Action

  38. Dispatcher View Store Action Server

  39. Let's make an App !! Will be easy !!!!

  40. None
  41. How to load data from server?

  42. <TopicTag /> <Feed /> <Document />

  43. A lot of trips between server and client /feed #

    get feed (documents ids) /documents/{id} # get a document /topics/{id} # get a topic Reuse the endpoint
  44. A big payload /feed_with_topics_max_tree_documents One custom endpoint that loads everything

  45. /feed?topics=true&max_documents=3 Multiple custom endpoints

  46. And what about: Pagination? Ordering? Error handling? Retry? Caching?

  47. Don't forget about writes: Conflicts? Write Coordination? Latency Compensation? State

    Sync?
  48. Nah... We can solve this! Let's combine X + Y

    + Z and solve it!
  49. None
  50. Want to introduce changes ??? First learn about the entire

    project … :(
  51. - How to fetch data - How to coordinate requests

    - How to coordinate writes - How to handle retries/errors - How to keep server/app in sync - How to update view with changes - How to cache data - How to keep that all in your head - How to …. - Your App What you had to think about:
  52. - What data to fetch - Your App What you

    want to think:
  53. Focus on what matters most! Relay make easy for developers

    to focus on the product and forget about data access nuances
  54. Declarative Colocation Mutations Principles

  55. Declarative The component describe what it want

  56. // Story.js export default class Story extends React.Component { render()

    { var story = this.props.story; return ( <View> <Image uri={story.author.profilePicture.uri} /> <Text>{story.author.name}</Text> <Text>{story.text}</Text> </View> ); } }
  57. fragment on Story { text, author { name, profilePicture {

    uri } } }
  58. export class Story extends Component { … } export default

    Relay.createContainer(Story, { fragments: { story: () => Relay.QL` fragment on Story { author { name profilePicture { uri } } text } `, }, });
  59. export class Story extends React.Component { ... } export default

    Relay.createContainer(Story, { fragments: { story: () => Relay.QL` fragment on Story { author { ... } } `, }, }); Colocation
  60. Easy to compose

  61. export class NewsFeed extends Component { … } export default

    Relay.createContainer(NewsFeed, { fragments: { feed: () => Relay.QL` fragment on Feed { stories(first: $count) { // fetch viewer's stories edges { // traverse the graph node { ${Story.getFragment('story')} // compose child fragment } } } } `, }, });
  62. class FeedRoute extends Relay.Route { static routeName = "FeedRoute"; static

    queries = { feed: () => Relay.QL` query { userFeed(uid: $userID) } `, }; } var feedRoute = new FeedRoute({userID: '123'}); Query Routes
  63. ReactDOM.render( <Relay.RootContainer Component={Feed} route={feedRouter} renderLoading={...} // optional renderFailure={...} // optional

    renderFetched={...} // optional />, domElement ); Root Containers
  64. A common problem: Pagination

  65. export default Relay.createContainer(NewsFeed, { initialVariables: { count: 3 }, fragments:

    { feed: () => Relay.QL` fragment on Feed { stories(first: $count) { ... } } `, }, });
  66. class NewsFeed extends React.Component { render() { ... } loadMore()

    { var count = this.props.relay.variables.count; this.props.relay.setVariables({ count: count + 5, }); } }
  67. followers(first: Int, last: String) { edges { node // the

    actual follower object, cursor // node identifier in the edge }, pageInfo { hasNextPage, lastCursor } } Pagination: How it works?
  68. export default Relay.createContainer(Story, { fragments: { story: () => Relay.QL`

    fragment on Story { author { name followers(first: $limit) @include(if: $isDesktop) { ... } } text } `, }, }); Directives
  69. Mutations

  70. getMutation() { ... } getFatQuery() { ... } getConfigs() {

    ... } getVariables() { ... } Defining a mutation
  71. export default class LikePostMutation extends Relay.Mutation { getMutation() { ...

    } getVariables() { ... } getConfigs() { ... } getFatQuery() { ... } } Defining the mutation
  72. getMutation() { return Relay.QL`mutation{ likePost // `likePost` mutation must be

    defined on the server }`; } getMutation()
  73. getVariables() { return { id: this.props.post.postId, like: this.props.like }; }

    getVariables()
  74. getConfigs() { return [{ type: 'FIELDS_CHANGE', fieldIDs: { post: this.props.post.id

    } }]; } getConfigs()
  75. getFatQuery() { return Relay.QL` fragment on LikePostPayload { post {

    id, likesCounter }, } `; } getFatQuery()
  76. export class Story extends React.Component { handleEditPost() { const onFailure

    = () => {}; const onSuccess = () => {}; Relay.Store.commitUpdate(new LikePostMutation({ post: this.props.post, like: true }), {onFailure, onSuccess}); } } Sending the mutation
  77. getOptimisticResponse() { return { post: { id: this.props.post.id, likesCounter: this.props.post.likesCounter

    + 1 } } } getOptimisticResponse()
  78. interface Node { id: ID! } type Post extends Node

    { id: ID!, … // post fields } Object Tracking:
  79. - Server-side rendering support - React Router integration - Babel

    Plugin - Active development: - Subscription (live) on the work - Client state is being discussed Important Details
  80. - Work well with others tools - Flux, Redux, etc

    - Patterns are still emerging Not a silver bullet
  81. Question?