Build a Message Board App in Vue

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:

create post button

When clicked, this button brings up the modal to create the new post:

new post modal

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”).