Data Fetching with GraphQL and Apollo
Get Everything You Ask For
Using GraphQL, something which previously would have meant several requests to different parts of the API to get specific pieces of information (and oftentimes some additional data which was not really needed but part of that API endpoint) now can be achieved through a single request, with easily customizable queries, to get all the necessary data - nothing more, nothing less.
Once a Schema, containing the definitions of the available Types, has been set up, you can begin querying your desired data.
The following Query,
{
hero {
name
appearsIn
}
}
could produce a result like this,
{
"data": {
"hero": {
"name": "R2-D2",
"appearsIn": [
"NEWHOPE",
"EMPIRE",
"JEDI"
]
}
}
}
GraphiQL is a GUI for editing and testing GraphQL queries and mutations.
For more information, see official documentation.
Apollo
Apollo provides an open-source set of tools and libraries centered around the GraphQL specification, which, in their own words, _ implement features, techniques and feedback from our partners, the community, and our own extensive research_.
For more information, see official documentation.
Working with Apollo
In order to maximise the performance of Apollo Client and ensure predictable behaviour of the cache, there are some important guidelines to following when working with it.
Make sure to fetch IDs
When querying for data from the API, make sure that you always remember to fetch the id
, if one exists for that the object you are requesting. If you do not do this, you may encounter some strange caching bugs.
Apollo Client normalises your data before storing it in the in-memory cache. To do this, it creates a unique identifier for each piece of data based on the id
and the __typename
of the object. If no id
was fetched, Apollo will instead use an index-based identifier when normalising the data. This can result in unexpected behaviour when fetching items if you sometimes fetch the id
and other times do not.
For more information on caching and normalisation of data, take a look at the Apollo Client docs
Do not mutate the cache
You must treat the Apollo Cache as immutable. The Flight framework enforces cache immutability with Apollo Client, as this gives major performance improvements. If you mutate the cache directly, an error will be thrown in your development environment.
So, what does this mean?
You should avoid directly writing data to the cache. For example, the following is destructive, because it pushes a new item on to the products
field:
const data = client.readQuery({
query: ProductQuery,
});
// Destructive! Don't do this!
data.products.push(newProduct);
client.writeQuery({
query: ProductQuery,
data,
});
This is because data
is a reference to the cache, not a copy of the cache.
Instead, you should create a new object from the cache and modify that, leaving the cache itself untouched. This is easy to do with the spread operator.
const data = client.readQuery({
query: ProductQuery,
});
// Create a *new object*
const newData = {
// Fill the new object with the existing data
...data,
// Replace the products array in this new object
products: [
// Fill the products array with the existing products
...data.products,
// Append the new product
newProduct,
],
};
client.writeQuery({
query: ProductQuery,
// Write these changes to the cache
data: newData,
});
Here, the original data
is untouched. Instead it is used to construct a new object, which is then written back to the cache.
Updating the cache after a mutation
The most common reason you might want to modify the cache is to update it after a mutation. Take a look at the official Apollo documentation for a tutorial on how to use update
when using mutations. Note that in the example, a todo item is written to the cache by taking the original todos
array and using Array.prototype.concat
to append the new todo. Array.prototype.concat is non-destructive, because it create a new array before appending the item. Thus, the cache is never mutated.