Apollo is a great interface for fetching and managing data through GraphQL. When we’re building applications with GraphQL, it’s important to manage data effectively to have a smooth development experience. This is where Apollo steps in, and acts as an abstraction layer over GraphQL (which is just a query language), and provides a robust platform for data fetching.
The power of Apollo lies with how much we get out of the box (it’s a lot). We don’t need to do lengthy custom setups or configurations and can get started right away. Here, we’ll be discussing the benefits and usage of Apollo Client with ReactJS, but it’s important to note that most of the same features are also available in Angular and Vue.
What is GraphQL?#
Before we dive deeper into Apollo, let’s briefly discuss GraphQL.
GraphQL is a query language, built by developers at Facebook that emphasizes predictable results by building schemas for building queries. We use a tree structure to fetch data through a single central API endpoint.
With traditional REST APIs, we are usually exposing multiple APIs for different purposes. For example, we might have an API to fetch books, and a different API to fetch authors. Similarly, we may have APIs to get a singular book or author. With GraphQL, we only hit a single endpoint, but we use fields to fetch only the data we need, such as only the name of the book, in a sea of different available parameters made available by the back-end.
By making a single request, our application can be fast even on slow mobile networks. The type system of GraphQL keeps the API structure robust, and the dev tools help us see the schema definitions to easily debug our requests. Moreover, it solves the problem of over-fetching and under-fetching, as we’re able to get exactly the data we need in one hit. Its optimistic UI and caching make all interaction with the server super fast and snappy.
How does it work on the server-side?#
On the server-side, we import the required libraries and create schemas for Queries and Mutations, respectively. This prevents us from spending time thinking of complex API flows. Queries are used to read data, whereas Mutations are used to create, update, or delete the data.
Here is a very basic example from the GraphQL documentation:
First, we build the schema where all our possible queries lie,
Now, we can create a root resolver that hosts all our functions corresponding to the queries we have created:
Finally, we can initialize our application with GraphQL.
The algorithm itself is very simple, it resolves each field before diving inside and doing the same until all the fields are resolved. After that, the result is returned in the required format.
We can also choose to create our GraphQL backend with apollo-server. It's easy to get started with, isn't tied to a specific GraphQL client in any way, and provides us some useful functionality for the backend as well including incremental updates.
Working with GraphQL on the front-end#
To describe and initiate our queries on the front-end, we can use the
graphql library to hook into our React components, this will give us our resultant data as props. More often, people choose to go with libraries such as Apollo and Relay. Here, we’ll be discussing how Apollo specifically provides us with a toolkit to make our lives easier when working with GraphQL.
Firstly, we need to include the required functions from apollo-client, apollo-link-http and apollo-cache-inmemory.
We can create a client instance through ApolloClient, where we’ll pass two things:
link: The endpoint of our GraphQL server. As mentioned before, this is the only endpoint we’ll be hitting, and the backend resolvers handle the rest.
cache: Apollo uses a caching system to store our queries for future use. We can call InMemoryCache and pass optional props to it to configure it even further.
We can do much more with queries like this, for example, we may choose to hit a different resolver to get all the books of a specific author.
As Apollo uses Context API under the hood, to enable it to access our components, we need to wrap our root element with ApolloProvider.
Next, we need to define our query. It’s better to create a folder structure where all our queries and mutations are in specific folders so it’s easier to navigate during development.
Here, we’ll create a
userQueryBooksByAuthor.js file, where we’ll define and export our query.
Now, we can import this in the file for our React component where we’ll be calling this query and use it with the hooks Apollo provides.
useLazyQuery allows us to query programmatically on events. For example, we click a button to load the books, in that case, the click handler function will look something like this:
And to call it:
Here, we’re passing the
authorName from our click function, but we could also have this come from anywhere, such as a state variable. If we wanted to run the query every time a component loads, we can call the
useQuery hook instead, in this way:
Now, all we need to do is conditionally render our data. We can simply do that in our return statement in this fashion:
Mutating data with Apollo Hooks is just as easy. We can call the
useMutation hook, pass in the required variables to update our data, and get our results back.
First, we initialize our hook and create our
useMutationUpdateAuthorName.js file in our mutations folder:
Now, we can call it conditionally. Like before, we’ll call the mutation on a button click.
And to call it:
refetchQueries parameter allows us to specify an array of previously defined queries to fetch once our mutation is complete. Although it is unnecessary in our case, just for the sake of example we’ll get all the books by the author again. Enabling the
awaitRefetchQueries waits until the mentioned queries have been called before marking the mutation as complete.
State Management Benefits:#
Typically, when we’re working with REST APIs, we need to have some sort of state management solution for our React apps. This is because we need to keep track of our data, loading states, and success/error responses. This can sometimes make our code harder to navigate.
However, Apollo provides us with caching as well as variables that hold the loading and API call result states directly. This means we don’t need to create further state variables, as we can just fetch them out of the query or mutation that we run as seen in the previous example.
Moreover, we don’t need to store the fetched data in state either. Since Apollo caches all the results, if we query for the same data again, it will avoid making unnecessary API calls and instead look into the memory first to find a cached version of the call.
Wrapping it up#
From experience, it's clear that GraphQL is being considered more day by day by developers against traditional REST servers. One common use-case I’ve encountered is fetching data from Content Management Systems, where a specific data model is defined. This makes it easy to think of our data in a tree structure and to fetch what we need based on conditions quickly and efficiently.
There’s no doubt that it allows developers to think of a different approach when handling data management between clients and servers. And Apollo is right at the forefront, providing even more features and possibilities to make things convenient for developers.