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

Crafting compelling real-time web experiences with GraphQL and React

3353b76e1480888ea7a482ebaa883dcb?s=47 Rob Crowley
September 21, 2018

Crafting compelling real-time web experiences with GraphQL and React

Users expect real-time data. They want their banking transaction notifications now. Their orders confirmed now. They need prices accurate as of now. They want their user experience to feel—connected. The world has moved to push and users are waiting for the data-driven experiences we create on the web to catch up.

GraphQL is Facebook’s response to this challenge and it is quickly proving itself as an attractive alternative to RESTful APIs for a wide range of contexts. GraphQL is a query language that provides a clean and simple syntax for consumers to interrogate your APIs. These queries are strongly typed, hierarchical and enable clients to retrieve only the data they need.

In addition to the familiar pull based request-response pattern, GraphQL provides a mechanism for clients to subscribe to real-time updates in the form of Subscriptions. Subscriptions are exciting as they represent the core ability we need to satisfy the connected experience that our users demand.

In this talk, we will take a hands-on look at GraphQL and see how it can be used to build real-time APIs that are a joy to use. Additionally, we will explore how to integrate a React client with GraphQL using the ultra flexible, community driven Apollo client. By the end of the session you will understand what is required to craft a compelling real-time user experience with GraphQL, and have the knowledge to deliver on these requirements.

3353b76e1480888ea7a482ebaa883dcb?s=128

Rob Crowley

September 21, 2018
Tweet

Transcript

  1. BUILDING COMPELLING REALTIME WEB EXPERIENCES @ROBDCROWLEY | ROBDCROWLEY

  2. ▪ GRAPHQL OVERVIEW ▪ THE CASE FOR REALTIME EXPERIENCES ▪

    THE SERVER STORY ▪ A CLIENT PERSPECTIVE ▪ MAKING IT ALL PERFORMANT
  3. None
  4. ♥ AND I’M FINE WITH THAT!

  5. MANY TYPES OF CLIENTS CAN CONSUME OUR APIS

  6. None
  7. HOW CAN EACH CLIENT RETRIEVE EXACTLY THE DATA IT REQUIRES

    IN A SINGLE ROUND TRIP TO THE SERVER?
  8. SHOW ME HOW GRAPHQL WORKS ALREADY!

  9. type Actor implements Node { id: ID! name: String appearedIn:

    [Film] netWorth: MonetaryAmount }
  10. let queryFilm = (_, {id}, {models}) => { // arbitrary

    code to retrieve film return models.film.get(id); } let filmTitle = film => { // just return property from root film return film.title; }
  11. THE ABSOLUTE MINIMUM YOU NEED TO KNOW (PROBABLY)

  12. None
  13. None
  14. query { film(id: “2”) { title characters { name actors

    { name } } } }
  15. query { film(id: “2”) { title characters { name actors

    { name } } } }
  16. query { film(id: “2”) { title characters { name actors

    { name } } } }
  17. query { film(id: “2”) { title characters { name actors

    { name } } } }
  18. query { film(id: “2”) { title characters { name actors

    { name } } } }
  19. ▪ IS A QUERY LANGUAGE FOR YOUR API ▪ ALLOWS

    CLIENTS TO CRAFT STRONGLY TYPED QUERIES ▪ FACILITATES RETRIEVING EXACTLY THE DATA YOU REQUIRE ▪ HAS POWERFUL FEATURES SUCH AS INTROSPECTION ▪ IS PRETTY COOL ☺
  20. WHY IS IT IMPORTANT?

  21. None
  22. CUSTOMERS EXPECT THEIR DATA TO BE AVAILABLE IN REALTIME. THIS

    HAS ALREADY HAPPENED AND WE NEED TO CATCH UP .
  23. None
  24. None
  25. GREAT WHEN THE DATA REFRESH RATE IS KNOWN OTHERWISE WE

    NEED TO BALANCE FRESHNESS AND CHATTINESS
  26. DATA REFRESH IS BASED ON OBSERVING STATE ON THE SERVER

    THINK INFINITELY FAST POLLING
  27. query @live { film(filmId: 2) { title description reviews {

    aggregateRating { average } } } }
  28. None
  29. DATA REFRESH IS BASED ON OBSERVING EVENTS READ-ONLY / NOT

    A REPLACEMENT FOR QUERIES
  30. None
  31. None
  32. None
  33. subscription onReviewAdded { reviewAdded { review { rating } film

    { title } } }
  34. mutation addReview { addReview(input: { clientMutationId: “MjpGaWxt” content: “Quirky and

    darkly funny”, rating: 9, filmId: 5 }) { clientMutationId } }
  35. { reviewAdded: { review: { rating:9 } film: { title:

    “Fargo” } } }
  36. USING GRAPHQL PLAYGROUND

  37. GRAPHQL PROVIDES A CONSISTENT INTERFACE FOR BOTH PUSH AND PULL

    INTERACTIONS
  38. None
  39. ▪ IS SUPPORTED VIA POLLING / SUBSCRIPTIONS / LIVE QUERIES.

    ▪ AFFORDS A CONSISTENT INTERFACE FOR SYNC AND ASYNC QUERIES VIA SUBSCRIPTIONS. ▪ IS VERY POWERFUL WHEN USED APPROPRIATELY.
  40. GRAPHQL FROM A CONSUMER PERSPECTIVE

  41. DX IS KEY TO DELIVERING A SUCCESS API

  42. None
  43. MINIMAL GRAPHQL CLIENT SUPPORTING NODE AND BROWSERS FOR SCRIPTS OR

    SIMPLE APPS
  44. import { request } from ‘graphql-request’ const query = `{

    film(filmId: “2”) { title description releasedOn } }` request(‘https://api.example.com/graphql’, query) .then(data => console.log(data))
  45. FACEBOOK’S FRAMEWORK FOR BUILDING DATA DRIVEN APPLICATIONS USING GRAPHQL

  46. None
  47. type Actor implements Node { id: ID! actorId: ID! name:

    String appearedIn: ActorFilmsConnection }
  48. type Genre implements Node { id: ID! name: String! description:

    String } type FilmGenresConnection { edges: [FilmGenresEdge] nodes: [Genre] pageInfo: PageInfo! totalCount: Int! } type FilmGenresEdge { cursor: String! node: Genre }
  49. None
  50. ULTRA-FLEXIBLE, FULL FEATURED, COMMUNITY-DRIVEN CLIENT

  51. USING APOLLO CLIENT + REACT

  52. POR QUÉ NO LOS DOS

  53. HOW FAST IS FAST ENOUGH?

  54. A SLOW RESOLVER WILL DELAY THE ENTIRE RESPONSE

  55. DEFERRED QUERIES ENABLE OPTIMIZED DATA LOADING BY PREVENTING SLOW FIELDS

    FROM DELAYING THE ENTIRE RESPONSE
  56. type Review implements Node @defer { id: ID! rating: Rating!

    content: String! @defer film: Film! createdAt: DateTime! }
  57. query { film(id: “5”) { title reviewsConnection { edges {

    node { rating content } } } } }
  58. { “data”: { “film”: { “title”: “Fargo”, “reviewsConnection”: { “edges”:

    [ { “node”: null } ] } } } } }
  59. { “path”: [ “film”, “reviewsConnection”, “edges”, 1, “node” ], “data”:

    { “rating”: 9, “content”: null } }
  60. { “path”: [ “film”, “reviewsConnection”, “edges”, 1, “node”, “content” ],

    “data”: { “content”: “Witty and funny” } }
  61. LOADING STATE TO THE RESCUE

  62. <Query query={REVIEW_QUERY}> {({ loading, error, data, loadingState }) => {

    if (loading) return <div>Loading...</div>; if (error) return <div>Error :(</div>; return ( <div> {loadingState.review.content ? data.review.content : “Review content pending...”} </div> ); }} </Query>
  63. PESISTED QUERIES REDUCE NETWORK TRAFFIC VOLUMES AND PROVIDE PROTECTION AGAINST

    PATHALOGICAL QUERIES
  64. new ApolloServer({ typeDefs, resolvers, engine: { apiKey: config.apolloEngineKey }, persistedQueries:

    { cache: new RedisCache({ host: config.queryCache.host, port: config.queryCache.port }) }, });
  65. hash:‘4fa973c’ hash:‘4fa973c’ query { film(id: “5”) { title } }

    { data: { film { title } } }
  66. hash:‘4fa973c’ { data: { film { title } } }

    query { film(id: “5”) { title } }
  67. HASHED QUERIES CAN BE SENT AS HTTP GET REQUESTS WITH

    APOLLO CLIENT TO ENABLE CACHING.
  68. yarn add apollo-link-persisted-queries

  69. new ApolloClient({ connectToDevTools: true, link: ApolloLink.from([ createPersistedQueryLink({ useGETForHashedQueries: true }),

    createHttpLink({ uri: “/graphql” }), ]), cache: new InMemoryCache() });
  70. type Review implements Node { id: ID! rating: Rating! content:

    String! film: Film! @cacheControl(maxAge: 60) createdAt: DateTime! }
  71. LOVE YOUR USERS BUT DON’T BLINDLY TRUST THEM

  72. None
  73. function resolver( root, args, context, info) { // l33t codez

    }
  74. LIMIT THE COMPLEXITY OF QUERIES SOLELY BY THEIR DEPTH.

  75. yarn add graphql-depth-limit

  76. query { film { title reviews { aggregateRating { average

    } } } }
  77. SET DATA CONSUMPTION LIMITS TO PROTECT AGAINST DOS ATTACKS

  78. yarn add graphql-cost-analysis

  79. type Review implements Node { id: ID! rating: Rating! content:

    String! @cost(complexity: 2) film: Film! @cacheControl(maxAge: 60) createdAt: DateTime! }
  80. None
  81. None
  82. None
  83. None
  84. ▪ ▪ ▪ ▪ ▪ ▪ ▪ ▪