DEV Community

Scott Molinari for Quasar

Posted on • Updated on

Quasar and Apollo - Client State without Vuex - Part 3

Part 3 - Vue-Apollo and its Working Parts - Mutations

If you've landed here inadvertently and haven't read the first part, please do.

This tutorial has 4 parts:

In the past two articles we got you up to speed with Quasar, Vue-Apollo and Apollo. We also covered querying for data. Now we'll go over how to manipulate data.

Mutations - Take 1

From the last article, we discussed how to get data into our components via GraphQL queries. On the other side of the coin, with mutations, we also have a form of querying, but for calling on special procedures that will manipulate the data source. I say a special form of querying, because like with queries, we can form how the response data will look like. Again, the data source mentioned is semi-irrelevant.

In our todo app, we have a number of mutations and we also have alternative ways to do them with vue-apollo.

The main function to carry out a mutation with vue-apollo is the, wait for it, the mutation function.

this.$apollo.mutate()

Let's look at the filter setting mutation first in our FilterBar.vue file.

So what is it we are looking at?

In...

lines 1-14, we have our template. Notice the @click event that triggers the setFilter() method.
lines 16-31, we have our imports of queries and mutations, our data initialization and our binding of our query for the filters (discussed in Part 2).
lines 33-45, we have our method, which calls this.$apollo.mutate().

In the mutation, we see the mutations.setActiveFilter binding, which is our actual mutation. It looks like this.

 export const setActiveFilter = gql`
   mutation setActiveFilter($name: String!) {
     setActiveFilter(name: $name) @client
   }
 `

Enter fullscreen mode Exit fullscreen mode

And, because of the @client directive, Apollo knows to use a local resolver (of the same name) to execute the mutation on our local data (more on this in Part 4).

If you go to our resolvers file, you can see the code for setActiveFilter.

  setActiveFilter: (_, args, { cache }) => {
  const data = cache.readQuery({
    query: queries.getFilters
  })
  data.filters.forEach(filter => {
    filter.name === args.name
      ? filter.active = true
      : filter.active = false
  })
  cache.writeData({ data })
}
Enter fullscreen mode Exit fullscreen mode

As you can see, we have two helper methods with our cache, readQuery and writeData. We'll get more into them and the whole resolver callback in Part 4.

In our resolver for setting the active filter, we simply find the filter in question via the filter's name property, set it and resave the cache with the new value.

If you look at the other resolvers for adding a new todo, editing a todo and deleting a todo, the pattern is the same.

read the cache -> manipulate results to the cache -> save the new version of the cache

In effect, you are in control of what the mutation does. The same goes for resolvers on the server, but that is a totally different topic to discuss for a different tutorial.

Type Definitions

If you haven't noticed them already and were wondering what the typeDefs.js file is under graphql/Todos, they will normally have the definitions of the object schema we use within our GraphQL system and are very important for the server-side. For client-side purposes though, they are mainly used for the Apollo Client Dev Tools (for Chrome). This is a handy tool to look into the cache and to also inspect your queries and mutations as they happen. Here is a screen of the tool.

Mutations - Take 2

In our first version of our mutation, we used a mutation and a resolver to manipulate our source of truth. Now, we'll have a look at a couple of other methods to do a mutation on the client.

Take a look at the TodoToggle.vue file.

What are we seeing different here?

In....

lines 35- 43, we are using the update option of the function. You'll see this callback injects the store (our cache object) and we use the store to query for our todos. We then find the todo we need and update it then write it back to the cache/ store.

Now have a look at the TodoToggle.alt.vue file. For brevity, we'll only show the main differences in the code.

What is different here?

In....

lines 1-15, we are using vue-apollo's <ApolloMutation> component to create the mutation. You'll notice it has two props. The mutation prop, which we give it the todoToggle mutation from our mutations. And the update prop, where we offer it the updateCache method on our component, which is the same as our update option used above.

If you wanted to, just like with the queries, you can also have your mutation GQL written in the component.

Something like....

<template>
  <ApolloMutation
    :mutation="gql => gql`
      mutation toggleTodo($id: String!) {
      toggleTodo(id: $id) @client
    }
  `"
Enter fullscreen mode Exit fullscreen mode

Or, you could also require a .gql file.

<template>
  <ApolloMutation
    :mutation="require('src/graphql/Todo/toggleTodo.gql')
    }
  `"
Enter fullscreen mode Exit fullscreen mode

Lastly, have a look at toggleTodo.alt2.vue. Again, the code below is shortened for brevity.

What is different here?

In...

lines 7-15, we now are using the readFragment method of the cache object. Fragments are a cool way to reuse sections of data, which you normally have in the breakdown of your component hierarchy. Although we aren't using them per se here in that manner, that is their main purpose. Code reuse and correctiveness. Please learn more about GraphQL Fragments.

Conclusion

There you have it. Mutations at their best. Although there are a number of paths leading to getting your mutations done client-side, whatever methods you choose, please always do it the same way all throughout your project. Keeping to standards is one key to clean and understandable code.

In Part 4, we'll be noting the trick to all of this along with some other good information about Apollo and its inner workings.

What do you think of mutating on the client-side with Apollo and GraphQL? Let us know in the comments below.

Top comments (0)