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

Crafting compelling real-time web experiences with GraphQL and React

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.

Rob Crowley

September 21, 2018
Tweet

More Decks by Rob Crowley

Other Decks in Programming

Transcript

  1. BUILDING COMPELLING REALTIME WEB EXPERIENCES
    @ROBDCROWLEY | ROBDCROWLEY

    View Slide

  2. ▪ GRAPHQL OVERVIEW
    ▪ THE CASE FOR REALTIME EXPERIENCES
    ▪ THE SERVER STORY
    ▪ A CLIENT PERSPECTIVE
    ▪ MAKING IT ALL PERFORMANT

    View Slide

  3. View Slide


  4. View Slide


  5. AND I’M FINE WITH THAT!

    View Slide

  6. MANY TYPES OF CLIENTS CAN CONSUME OUR APIS

    View Slide

  7. View Slide

  8. HOW CAN EACH CLIENT RETRIEVE EXACTLY THE DATA IT REQUIRES IN
    A SINGLE ROUND TRIP TO THE SERVER?

    View Slide

  9. SHOW ME HOW GRAPHQL WORKS ALREADY!

    View Slide

  10. type Actor implements Node {
    id: ID!
    name: String
    appearedIn: [Film]
    netWorth: MonetaryAmount
    }

    View Slide

  11. 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;
    }

    View Slide

  12. THE ABSOLUTE MINIMUM YOU NEED TO KNOW (PROBABLY)

    View Slide

  13. View Slide

  14. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  20. ▪ 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 ☺

    View Slide

  21. WHY IS IT IMPORTANT?

    View Slide

  22. View Slide

  23. CUSTOMERS EXPECT THEIR DATA TO BE AVAILABLE IN REALTIME.
    THIS HAS ALREADY HAPPENED AND WE NEED TO CATCH UP
    .

    View Slide

  24. View Slide

  25. View Slide

  26. GREAT WHEN THE DATA REFRESH RATE IS KNOWN
    OTHERWISE WE NEED TO BALANCE FRESHNESS AND CHATTINESS

    View Slide

  27. DATA REFRESH IS BASED ON OBSERVING STATE ON THE SERVER
    THINK INFINITELY FAST POLLING

    View Slide

  28. query @live {
    film(filmId: 2) {
    title
    description
    reviews {
    aggregateRating {
    average
    }
    }
    }
    }

    View Slide

  29. View Slide

  30. DATA REFRESH IS BASED ON OBSERVING EVENTS
    READ-ONLY / NOT A REPLACEMENT FOR QUERIES

    View Slide

  31. View Slide

  32. View Slide

  33. View Slide

  34. subscription onReviewAdded {
    reviewAdded {
    review {
    rating
    }
    film {
    title
    }
    }
    }

    View Slide

  35. mutation addReview {
    addReview(input: {
    clientMutationId: “MjpGaWxt”
    content: “Quirky and darkly funny”,
    rating: 9,
    filmId: 5 }) {
    clientMutationId
    }
    }

    View Slide

  36. {
    reviewAdded: {
    review: {
    rating:9
    }
    film: {
    title: “Fargo”
    }
    }
    }

    View Slide

  37. USING GRAPHQL PLAYGROUND

    View Slide

  38. GRAPHQL PROVIDES A CONSISTENT INTERFACE FOR BOTH PUSH AND
    PULL INTERACTIONS

    View Slide

  39. View Slide

  40. ▪ IS SUPPORTED VIA POLLING / SUBSCRIPTIONS / LIVE
    QUERIES.
    ▪ AFFORDS A CONSISTENT INTERFACE FOR SYNC AND
    ASYNC QUERIES VIA SUBSCRIPTIONS.
    ▪ IS VERY POWERFUL WHEN USED APPROPRIATELY.

    View Slide

  41. GRAPHQL FROM A CONSUMER PERSPECTIVE

    View Slide

  42. DX IS KEY TO DELIVERING A SUCCESS API

    View Slide

  43. View Slide

  44. MINIMAL GRAPHQL CLIENT SUPPORTING NODE AND BROWSERS FOR
    SCRIPTS OR SIMPLE APPS

    View Slide

  45. 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))

    View Slide

  46. FACEBOOK’S FRAMEWORK FOR BUILDING DATA DRIVEN APPLICATIONS
    USING GRAPHQL

    View Slide

  47. View Slide

  48. type Actor implements Node {
    id: ID!
    actorId: ID!
    name: String
    appearedIn: ActorFilmsConnection
    }

    View Slide

  49. 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
    }

    View Slide

  50. View Slide

  51. ULTRA-FLEXIBLE, FULL FEATURED, COMMUNITY-DRIVEN CLIENT

    View Slide

  52. USING APOLLO CLIENT + REACT

    View Slide

  53. POR QUÉ NO LOS DOS

    View Slide

  54. HOW FAST IS FAST ENOUGH?

    View Slide

  55. A SLOW RESOLVER WILL DELAY THE ENTIRE RESPONSE

    View Slide

  56. DEFERRED QUERIES ENABLE OPTIMIZED DATA LOADING BY PREVENTING
    SLOW FIELDS FROM DELAYING THE ENTIRE RESPONSE

    View Slide

  57. type Review implements Node @defer {
    id: ID!
    rating: Rating!
    content: String! @defer
    film: Film!
    createdAt: DateTime!
    }

    View Slide

  58. query {
    film(id: “5”) {
    title
    reviewsConnection {
    edges {
    node {
    rating
    content
    }
    }
    }
    }
    }

    View Slide

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

    View Slide

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

    View Slide

  61. {
    “path”: [
    “film”,
    “reviewsConnection”,
    “edges”,
    1,
    “node”,
    “content”
    ],
    “data”: {
    “content”: “Witty and funny”
    }
    }

    View Slide

  62. LOADING STATE TO THE RESCUE

    View Slide


  63. {({ loading, error, data, loadingState }) => {
    if (loading) return Loading...;
    if (error) return Error :(;
    return (

    {loadingState.review.content
    ? data.review.content
    : “Review content pending...”}

    );
    }}

    View Slide

  64. PESISTED QUERIES REDUCE NETWORK TRAFFIC VOLUMES AND
    PROVIDE PROTECTION AGAINST PATHALOGICAL QUERIES

    View Slide

  65. new ApolloServer({
    typeDefs,
    resolvers,
    engine: {
    apiKey: config.apolloEngineKey
    },
    persistedQueries: {
    cache: new RedisCache({
    host: config.queryCache.host,
    port: config.queryCache.port
    })
    },
    });

    View Slide

  66. hash:‘4fa973c’
    hash:‘4fa973c’
    query {
    film(id: “5”) {
    title
    }
    }
    {
    data: {
    film {
    title
    }
    }
    }

    View Slide

  67. hash:‘4fa973c’
    {
    data: {
    film {
    title
    }
    }
    }
    query {
    film(id: “5”) {
    title
    }
    }

    View Slide

  68. HASHED QUERIES CAN BE SENT AS HTTP GET REQUESTS WITH APOLLO
    CLIENT TO ENABLE CACHING.

    View Slide

  69. yarn add apollo-link-persisted-queries

    View Slide

  70. new ApolloClient({
    connectToDevTools: true,
    link: ApolloLink.from([
    createPersistedQueryLink({
    useGETForHashedQueries: true
    }),
    createHttpLink({ uri: “/graphql” }),
    ]),
    cache: new InMemoryCache()
    });

    View Slide

  71. type Review implements Node {
    id: ID!
    rating: Rating!
    content: String!
    film: Film! @cacheControl(maxAge: 60)
    createdAt: DateTime!
    }

    View Slide

  72. LOVE YOUR USERS BUT
    DON’T BLINDLY TRUST THEM

    View Slide

  73. View Slide

  74. function resolver(
    root,
    args,
    context,
    info) {
    // l33t codez
    }

    View Slide

  75. LIMIT THE COMPLEXITY OF QUERIES SOLELY BY THEIR DEPTH.

    View Slide

  76. yarn add graphql-depth-limit

    View Slide

  77. query {
    film {
    title
    reviews {
    aggregateRating {
    average
    }
    }
    }
    }

    View Slide

  78. SET DATA CONSUMPTION LIMITS TO PROTECT AGAINST DOS ATTACKS

    View Slide

  79. yarn add graphql-cost-analysis

    View Slide

  80. type Review implements Node {
    id: ID!
    rating: Rating!
    content: String! @cost(complexity: 2)
    film: Film! @cacheControl(maxAge: 60)
    createdAt: DateTime!
    }

    View Slide

  81. View Slide

  82. View Slide

  83. View Slide

  84. View Slide









  85. View Slide