Manage Local State using Apollo by extending the GraphQL Schema on the Client

Nik Graf
InstructorNik Graf
Share this video with your friends

Social Share Links

Send Tweet

With the introduction of apollo-link-state Apollo for the first time introduced a way to manage local state through GraphQL queries and mutations. This can be achieved using the @client directive.

In this lesson we will leverage this feature to keep track of starred recipes and store the information in the localStorage.

Instructor: [00:00] In this lesson, we want to extend our application to allow users starring their favorite recipes. Therefore, we want to add an isStarred field to the recipe. Since our schema doesn't support this field, we plan to keep this information solely on the client. Namely, store it in a local storage.

[00:19] To query a field only on the client, we can leverage the @client decorator, and add it to a field in a query or a mutation. Once added, this field is never queried on the remote endpoint, yet there is no resolve with resolveTheField.

[00:36] We need to provide one. In our app.js file, we create resolvers object, and for the type, recipe, add an isStarred resolver. Temporarily return the value false for all recipes. We then can use the resolver's object, and pass it into the client state property, during the Apollo client initialization.

[00:59] This is all we need to already being able to retrieve the isStarred value in our recipes list. We render a star next to each of the titles, and the color should change, depending on the state. Orange, if the recipe is starred, and gray if it's not.

[01:24] Let's refresh the page, and verify that all stars are inactive. Great. Next up, we need a mutation, allowing us to update the isStarred field of a recipe. Therefore, we extend our client-side resolvers with an updateRecipeStarred mutation. Our plan is to store a starred recipes array in the local storage. Before we update it, we retrieve the list.

[02:02] In case the variable isStarred is set to true, we append the current ID. In case it's set to false, we filled out existing recipe ID. In the end, we return an object with the type name and the isStarred value. This is useful in case a developer wants to query the updated value. Since we now have the mutation, and know how we store the starred recipes, you also can update the retrieving resolver.

[02:47] In the resolver, we check if the starred recipes array includes the ID of the current parent recipe, and return the result. At this point, we have everything we need to start using our mutation. Therefore, we add a new mutation to the recipes.js file.

[03:16] The mutation accepts two variables, ID and isStarred. This matches what we implemented in our mutation. In our case, we won't query any data from the mutation, but we have to append the @client decorator to make sure this mutation is resolved only on the client. After that, we import the mutation component and wrap the star span with it.

[03:44] Then we pass in our updateRecipeStarred mutation. Since this mutation affects the result of our recipe query, we provide the two refetch queries. Once again, set awaitRefetchQueries to true. After that, we replace the span with a button and add an onClick handler. Once clicked, we invoke the mutation with the current recipe ID and the reverse isStarred value.

[04:33] Next, we add a class named starButton. To make sure the star looks actually nice, I prepared this class name before the lesson, and added the stars to the index.html file. Depending on the loading state, we add in animation. Last, but not least, render text in case an error occurred. Now, we should be able to star our recipes.

[05:07] We refresh the page and give it a try. Voila, works like a charm. It even works after checking the vegetarian filter. To verify that our stars were cached in the local storage, we refresh the page once again. Great, our two recipes are still starred.