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:
- Part 1 - Getting Started
- Part 2 - Vue-Apollo and its Working Parts - Queries
- Part 3 - Vue-Apollo and its Working Parts - Mutations *(You are here now)*
- Part 4 - The Trick and the Rest of (Vue-)Apollo
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
}
`
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 })
}
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
}
`"
Or, you could also require a .gql
file.
<template>
<ApolloMutation
:mutation="require('src/graphql/Todo/toggleTodo.gql')
}
`"
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)