Overview
GraphQL is a popular API query language for clients to access data on servers. Cresta uses GraphQL in different ways to act as a bridge between our frontend and backend systems. This blog will discuss how a developer familiar with Redux can migrate over to GraphQL using the popular Apollo Client library to take advantage of GraphQL features.
At Cresta, simplicity and speed are a couple of our core values for our development team. In a typical application with client and server components, client state management is typically different from server state management. For example, it’s popular to use Redux for client state management and REST API for server state manipulation. However, if GraphQL is used for both purposes, we gain greater simplicity.
This guide offers insights and tips for developers like yourself, who have an understanding of Redux using GraphQL and Apollo Client as a client state store. We’ll explore a use case by taking part in an existing redux react chat client and partially converting its message history component while using GraphQL and Apollo Client. Let’s dive in.
Why GraphQL?
Many libraries are available for state management and synchronizing data with the server, so why use GraphQL?
On the one hand, there are Redux, MobX, and so on, which do an excellent job of client state management but leave the server synchronization as a completely separate task to be implemented by you (or by using a library such as Redux-API to ease this implementation).
On the other hand, there are tools like Firebase, Realm Sync, and PouchDB+CouchDB, which abstract and automate database synchronization so that you only need to deal with client data. However, you also loses control of the data synchronization process. The abstraction makes it hard to control what data synchronizes first. It could skip synchronization of non-critical data altogether. Furthermore, this approach locks the application to a specific (and sometimes proprietary) database system.
By using GraphQL, you get a more balanced solution between the two sets of tool types. Because both the client and server speak the same language, synchronization becomes easier, yet you still maintain control. For the client state, you may use a library like Apollo Client. A GraphQL server defines and exposes the Query API layer so that clients can access/save data with it. On the server-side, a variety of choices exist.*
Additionally, it should be noted that GraphQL is an open standard, which prevents vendor lock-in and makes it easier to swap out parts of the system in the future as the application evolves.
Intro to Apollo Client
As official documentation describes, Apollo Client is a complete state management library with GraphQL. Interestingly, one of its key features is zero-config caching, making it easy to use as a state store. Furthermore, Apollo Client already comes with support for React applications.
Dispelling a Myth Regarding Apollo Client
Apollo Client is frequently used with a GraphQL backend, so some programmers believe the myth that it can ONLY be used with a GraphQL backend. The truth of the matter is – Apollo Client can absolutely be used as a client-only, in-browser store without any backend. Below is a basic code sample that initiates a client with only in-memory caching, without specifying any GraphQL backend to connect to:
More specifically, in the code example presented above, an in-memory cache is provided to the client to connect to, and the local state can be stored in this cache, according to this Apollo Client Documentation. It’s an easy assumption to make, but Apollo Client can be used either with GraphQL or as an in-browser store without any backend.
Chrome Extension Tool
To make debugging more manageable, the Apollo Client Devtools may be installed in your Chrome browser to watch queries and client cache state as you debug the code.
Now that we’ve addressed a common myth and have the tools we need, let’s begin the use case example of replacing Redux store with Appollo Client.
Replacing Redux Store with Apollo Client
Example – Displaying State
Let’s look at one example of a react-redux component to show message history in a redux chat app. Note how react-redux passes state threads into the component as a react props value:
In this example, the threads state contains a dictionary of arrays, with the recipient being the key. Each array contains elements with key (numeric sequence number), from (string identifying the sender) and text (string for the message text) for that recipient.
Examine the screenshot below to see how the new code looks. This shows how you can convert this component using Apollo Graph QL (you can test it here).
The above screenshot shows that by using the Apollo Client useQuery method, the client GraphQL cache can be queried, and the return values can be passed as props to the react UI component to render.
This example is not super helpful, however it does render a static, hardwired state into the UI. To realize the full power of using Apollo Client cache as a store, let’s explore how to modify the stored values in the in-memory cache.
Making Modifications to the Store
To make modifications to the store Redux, you can use Actions and Reducers. However, when Apollo Client cache is used as a store, the way to make modifications is slightly different:
- A query is specified: this limits the scope of data that is modified.
- You can invoke the readQuery method on the cache to read existing data from the query, with the returned result being similar to the state passed into a reducer.
- This data (similar to a reducer) can then be modified by creating a new object representing the new state and then written back into the cache by the writeQuery method.
Example – Modifying State
The above example shows how to access the store, but what about modifying the store when new messages are received? In this Redux example, whenever a new message is received, the reducer code updates this threads Redux state by concatenating the new message to the array corresponding to the recipient. See below:
The corresponding, converted code with Apollo Client is below, and you can test it here.
Note the following:
- The Apollo GraphQL client cache acts as an in-memory state store similar to the Redux store.
- To access its state, use `readQuery` on the cache first.
- The `writeQuery` call can do dispatching actions on the “store”
Comparison – Pros and Cons
As shown in the comparison table below, the advantage of using Apollo Client is by simplification – that is, it’s the opportunity to unify usage of GraphQL for both client state store and interactions with backends.
Apollo Client | Redux |
Pros:
|
Pros:
|
Cons:
|
Cons:
|
Next Steps
This guide explains how to convert part of a simple, open-source Redux chat app from Redux to Apollo Client. However, you can apply the concepts explained to other Redux applications too.
You can clone the sample converted chat app at this GitHub link. Feel free to clone, modify and experiment.
Below are some next steps to take this to the next level:
- Choose a GraphQL backend out of the variety of choices available*
- Connect to a GraphQL backend: After you choose a GraphQL backend, pass the link parameter to the ApolloClient constructor. The same web application is now easily converted to establish a bridge to a GraphQL backend.
- Explore fragments: as opposed to queries, “fragments” define pieces of data that can be re-used in multiple places.
- In addition to the message history example, take the existing redux-based chat application and convert the whole application to use Apollo Client instead.
- Explore apollo-cache-persist to save the store into browser local storage.
- Explore Optimistic UI to get the best of both worlds – fast client cache for UI, and backend for synchronized storage.
Let us know your thoughts and whether you’d like to see more on this topic.
*Examples of GraphQL backends:
- Hasura
- PostGraphile
- Apollo Server
- Prisma
- dgraph.io (SaaS)
- FaunaDB (SaaS)