GraphQL

πŸ’£

πŸ’£

Apollo Caching and API Design

We define a schema

type Query {
  posts: [Post]
  authors: [Author]
  ...
}

type Author {
  id: ID!
  firstName: String
  lastName: String
  posts: [Post]
}

type Post {
  id: ID!
  title: String
  author: Author
  votes: Int
}

..write queries..

query postsForAuthor {
  author(id: "1") {
    id
    firstName
    lastName
    posts {
      id
      title
    }
  }
}
{
  "author": {
    "id": "1",
    "firstName": "Dave",
    "lastName": "A",
    "posts": [{
      "id": "5",
      "title": "GraphQL",
    }]
  }
}

..and mutations.

mutation addPost {
  addPost(input:{
    authorId:"1",
    title: "More!"
  }) {
    post {
      id
      title
    }
  }
}
{
  "addPost": {
    "post": {
      "id": "6",
      "title": "More!"
    }
  }
}

We use great tooling..

Apollo Helps with..

  • Managing fetching and error state
  • Response Normalization
  • Client-side Caching

..caching (not magic)!

Caching is easy..!

query postsForAuthor {
  author(id: "1") {
    id  #### ALWAYS FETCH ID
    firstName
    lastName
    posts {
      id #### ALWAYS FETCH ID
      title
    }
  }
}
query listOfAuthors {
  id #### ALWAYS FETCH ID
  firstName
  lastName
}

πŸ’£

πŸ’£

πŸ’£

mutation changePost {
  updatePost(input:{
    id:"1",
    title: "Update this!"
  }) {
    post {
      id ### ALWAYS FETCH ID
      title
    }
  }
}

πŸ’£

πŸ’£

πŸ’£

πŸ’£

When removing a member to a list..

query postsForAuthors {
  author(id: "1") {
    id
    firstName
    lastName
    posts { ### NOT REMOVED
      id
      title
    }
  }
}
mutation removePost {
  removePost(input:{
    authorId:"1",
    postId: "6"
  }) {
    post { # DATA FOR REMOVED RECORD
      id
      title
    }
  }
}

πŸ’£

πŸ’£

..we can manually remove w Apollo..

<Mutation
  mutation={REMOVE_POST}
  update={(cache, { data: { removePost } }) => {
    const { todos } = cache.readQuery({ query: GET_POSTS });
    cache.writeQuery({
      query: GET_POSTS,
      data: { posts: posts.filter((post) => post.id == removePost.post.id) }
    });
  }}
>

..or design to refetch the list (better)

query postsForAuthors {
  author(id: "1") {
    id
    firstName
    lastName
    posts {
      id
      title
    }
  }
}
mutation removePost {
  addPost(input:{
    authorId:"1",
    title: "Sup"
  }) {
    post {
      id
      title
    }
    author {
      id
      posts { # NOW UPDATED!
        id # GOTTA GET THOSE ID!
      }
    }
  }
}

When adding to a list watch out..

query postsForAuthors {
  author(id: "1") {
    id
    firstName
    lastName
    posts {
      id
      title
      votes # WE USE VOTES HERE
            # BUT NOT ELSEWHERE
            # SUBTLE BUG ALERT!
    }
  }
}
mutation addPost {
  addPost(input:{
    authorId:"1",
    title: "Stuff"
  }) {
    post {
      id
      title
      # DANGER NOT FETCHING FIELDS
      # USED IN ANOTHER QUERY
    }
    author {
      id
      posts { # STILL REFTCH LIST
        id    # MEMBERSHIP
      }
    }
  }
}

πŸ’£

πŸ’£

πŸ’£

..fragments help fetch the right fields.

query postsForAuthors {
  author(id: "1") {
    id
    firstName
    lastName
    posts {
      ...allPostFields
    }
  }
}

# FRAGMENTS HELP KEEP CONSISTENCY
fragment allPostFields on Post {
  id
  title
  voteCount
}
mutation removePost {
  addPost(input:{
    authorId:"1",
    title: "Stuff"
  }) {
    post {
      # NO ERRORS NOW!
      ...allPostFields
    }
    author {
      id
      posts {
        id
      }
    }
  }
}

What's happening Under the Hood?

  • React-apollo is just a wrapper around the pure JavaScript Apollo client
  • When we wire a component into a query, a WatchQueryΒ is created

What's happening Under the Hood?

  • Default fetch policy is `cache-and-network`
  • Fires query if data is not cached
  • Denormalizes response
  • Stores denormalized data in cache
  • Reads data from cache for component
  • Any changes to cached data from other queries or mutations will be updated in this component without re-fetching from the server

What's happening Under the Hood?

  • When Apollo reads cached data for the component, it uses `graphql-anywhere` to fire a graphql query against the cache
  • If the response is inconsistent or incomplete, Apollo doesn't handle this well

Let's look at some example code

GraphQL is powerful,

Apollo is your friend,

API design is important!

πŸ’£

πŸ’£

GraphQL, Apollo Caching and API Design!

By dvndrsn

GraphQL, Apollo Caching and API Design!

A mini pecha kucha on how to design better mutations to work with Apollo caching.

  • 935