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,

Copy
Copied
{
  hero {
    name
    appearsIn
  }
}

could produce a result like this,

Copy
Copied
{
  "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:

Copy
Copied
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.

Copy
Copied
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.

Copyright © Norce 2023. All right reserved.