GraphQL Mutations in Apollo Vue
Working through the tutorial to this point gives you a working UI that you can use to query the sample data that you added, but doesn’t give you a UI to add new posts.
To add new posts, you’ll need to start using the ApolloMutation and hook it into our UI.
GraphQL mutations
Let’s start by creating a gql file at src/graphql/mutations/addpost.gql
, and add the below mutation:
mutation addPost($post: AddPostInput!) {
addPost(input: [$post]) {
post {
id
title
text
tags
datePublished
category {
id
name
}
author {
username
displayName
avatarImg
}
commentsAggregate {
count
}
}
}
}
The boilerplate to use a query is to use the query as part of loading the component, as in the following example:
<template>
<ApolloQuery :query="...">
<template v-slot="{ results: { data, loading, error }}">
<div v-if="loading"> /* render loading indicator */ </div>
<div v-else-if="error"> /* handle error */ </div>
<div v-else>
// layout using 'data'
</div>
</template>
</ApolloQuery>
</template>
However, mutations work differently. To use a mutation, you use the hook to create a function that actually runs the mutation and configure that with callback functions that execute after the mutation completes. Accordingly, the boilerplate for a mutation is as follows:
<template>
<ApolloQuery
:mutation="..."
:variables="{
name
}"
@done="onDone"
>
<template v-slot="{ mutate, loading, error }">
</template>
</ApolloQuery>
</template>
With this syntax, calling mutate()
executes the mutation
with the passed-in post data, and after the GraphQL mutation returns, the
@done function is executed.
Layout for the mutation
First of all, we will create a modal component that will pop up when we want to create a new post. Create a new component at src/components/CreatePostModal.vue
.
<template>
<ApolloMutation
:mutation="require('@/graphql/mutations/addpost.gql')"
:variables="{ title, tags, text, category: { name: category } }"
@done="() => $emit('input', false)"
>
<template v-slot="{ mutate }">
<sui-modal
:open="value"
@input="v => $emit('input', v)"
>
<sui-modal-header>Create Post</sui-modal-header>
<sui-modal-content>
<sui-modal-description>
<sui-form>
<sui-form-field>
<label>Title</label>
<input
placeholder="Type title..."
v-model="title"
/>
</sui-form-field>
<sui-form-field>
<label>Category</label>
<input
placeholder="Type title..."
v-model="category"
/>
</sui-form-field>
<sui-form-field>
<label>Tags (optional)</label>
<input
placeholder="Enter space separated tags..."
v-model="tags"
/>
</sui-form-field>
<sui-form-field>
<label>Your Message</label>
<textarea
rows="3"
placholder="Enter your message..."
v-model="text"
/>
</sui-form-field>
</sui-form>
</sui-modal-description>
</sui-modal-content>
<sui-modal-actions>
<sui-button color="black" @click="$emit('input', false)">
Cancel
</sui-button>
<sui-button
content="Submit"
labelPosition="right"
icon="checkmark"
@click="mutate"
positive
/>
</sui-modal-actions>
</sui-modal>
</template>
</ApolloMutation>
</template>
<script>
export default {
props: {
value: Boolean
},
data() {
return {
title: "",
category: "",
tags: "",
text: ""
}
}
}
</script>
We are linking the form inputs to the variables object by using Vue variables. The Submit button calls the mutate
function of the ApolloMutation
when clicked, which will trigger the mutation. We also have the mutation close the modal after mutations.
Now we have a modal for creating forms and is linked to mutations, but we can’t see it. Let’s link the header button to this modal in src/component/Header.vue
, as seen below:
<template>
<div class="ui clearing segment header-seg">
<h3 class="ui right floated header header-seg-right">
<span>
<sui-button class="dgraph-btn" @click="showCreateModal = true">Create Post</sui-button>
</span>
</h3>
<h3 class="ui left floated header header-seg-left">
<router-link to="/">
<div class="flex">
<span>
<sui-image size="tiny" src="/diggy.png" class="mr-5" />
</span>
<div>
<p class="header-text">Dgraph</p>
<p class="t-size">DISCUSS</p>
</div>
</div>
</router-link>
</h3>
<CreatePostModal v-model="showCreateModal" />
</div>
</template>
<script>
import CreatePostModal from './CreatePostModal'
export default {
components: { CreatePostModal },
data() {
return {
showCreateModal: false
}
}
}
</script>
We’ve linked the create post button to the modal using the showCreateModal
variable.
The last improvement we can make to have the modal load the list of categories and make the category selection into a dropdown. To do this, we will need to combine both the ApolloQuery
and the ApolloMutation
components.
To do that, let’s create src/graphql/queries/categories.gql
with the below query:
{
queryCategory {
id
name
}
}
Now, let’s wrap the CreatePostModal
in a ApolloQuery
.
<template>
<ApolloQuery
:query="require('@/graphql/queries/categories.gql')"
>
<template v-slot="{ result: { data: categoryData } }">
<ApolloMutation
:mutation="require('@/graphql/mutations/addpost.gql')"
:variables="{ title, tags, text, category: { name: category } }"
@done="() => ''"
>
<template v-slot="{ mutate }">
<sui-modal
:open="value"
@input="v => $emit('input', v)"
>
<sui-modal-header>Create Post</sui-modal-header>
<sui-modal-content>
<sui-modal-description>
<sui-form>
<sui-form-field>
<label>Title</label>
<input
placeholder="Type title..."
v-model="title"
/>
</sui-form-field>
<sui-form-field>
<label>Category</label>
<sui-dropdown
placeholder="Choose a category"
:options="categoryData.queryCategory.map(c => ({ text: c.name }))"
v-model="category"
/>
</sui-form-field>
<sui-form-field>
<label>Tags (optional)</label>
<input
placeholder="Enter space separated tags..."
v-model="tags"
/>
</sui-form-field>
<sui-form-field>
<label>Your Message</label>
<textarea
rows="3"
placholder="Enter your message..."
v-model="text"
/>
</sui-form-field>
</sui-form>
</sui-modal-description>
</sui-modal-content>
<sui-modal-actions>
<sui-button color="black" @click="$emit('input', false)">
Cancel
</sui-button>
<sui-button
content="Submit"
labelPosition="right"
icon="checkmark"
@click="mutate(); $emit('input', false)"
positive
/>
</sui-modal-actions>
</sui-modal>
</template>
</ApolloMutation>
</template>
</ApolloQuery>
</template>
<script>
export default {
props: {
value: Boolean
},
data() {
return {
title: "",
category: null,
tags: "",
text: ""
}
}
}
</script>
The modal is now set up with a list of possible categories for the post by first querying to find the existing categories and populating a dropdown from that.
All of this adds a Create Post button to the header, along with supporting logic:
When clicked, this button brings up the modal to create the new post:
This Step in GitHub
This step is also available in the tutorial GitHub repo and is this code diff.
You can run the app using the yarn start
command, and then navigate to http://localhost:3000
to see the post list on the home screen. Then, you can click
Create Post to add a new post to the backend GraphQL database. After
submitting the post, you’ll see it in the post list.
The user the post is added for is hard-coded in this step (to “TestUser”).