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
- Query
- Fragments
- MutationsΒ (styles)
- More mutationsΒ (prices)
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.
- 1,022