VueJS GraphQL Tutorial: Build a Polls App

By Amenallah Hsoumi - Created on May 30, 2019

In this VueJS GraphQL tutorial, we will be building a polls app, the idea is pretty simple, the user will have the ability to create a poll. Then, other users can vote for one of the options and see real-time results.

We will be using a boilerplate that I have created in an earlier tutorial. However, if you’re comfortable using other boilerplates, following this tutorial and implementing the code will be easy.

The Story

Our idea is very simple. We will basically have:

The code for this tutorial is available on GitHub.

Project Setup

We will start by setting up our project step by step using the boilerplate.

Setup

First of all, let’s clone the boilerplate from Github:

$> git clone https://github.com/LazyFatArrow/vue-apollo-mongo-boilerplate.git

Then, we will cd inside the cloned folder and copy the .env.example file into a new file called .env and change the MongoDB database name:

This will allow our server and client to recognize our configuration.

Next, we need to install the required dependencies, we can do that by just running:

$> yarn

As a result, the dependencies will be installed and thus, we can start our server:

$> yarn server:dev

Similarly, we can start the client by running in another terminal:

$> yarn web:dev

If we go to localhost:4000 you should see the GraphQL playground:

Vue.js GraphQL Tutorial

And if we go to localhost:8080 you should see the example Vue app:

VueJS GraphQL Tutorial default app
Click to expand

Understanding the structure

We have two main folders:

For the backend, we have our entry file, called index.js which does these things in order:

  1. Loads and merges all the files ending with .graphql from the schema folder
  2. Loads all the resolvers from the resolvers folder by importing resolvers/index.js
  3. Connects to MongoDB
  4. Launches Apollo-server on port 4000

The main business logic will live inside the services folder, and we will use all the methods we create there inside our resolvers, more on this later.

So without further ado, let’s start building!

Building the Backend with Node

The first thing we will do is to create the backend of our project. This will provide GraphQL queries, mutations, and subscriptions for the consumption by our frontend later.

Our GraphQL types and Mongoose models

We can start by creating the Mongoose model. The poll model will be like this:

server/models/poll.model.js

Notice that first, we didn’t create an options model, since we can just embed options inside the poll model directly.

Furthermore, we have used sub-documents, this is because we want each option to have an id, so that later when we want to vote for it we can identify it directly in our options array:

Alright, we got our model set up, now we will create the GraphQL schemas.

Starting with the Option type, let’s create Option.graphql inside server/schema :

Pretty simple schema, using the predefined GraphQL types. Likewise, we do the same for the Poll type:

As you can see, we have specified that our options attribute should be a list of the Option type, also using exclamation points we are forcing the existence of the options list and the content inside it, in other words, neither the list nor any of its content shall be null.

We don’t need to do anything else, our current project setup will pick up the new types. If we navigate to the playground we can see our new types on the right:

Creating Polls

Next, we will create our first mutation, which is the createPoll mutation. Using it will allow us to insert new polls in the database.

First, we will create the mutation inside the Mutation type in the Mutation.graphql file:

In addition, we will create the resolver for the mutation that will call a service which will handle inserting the data.

Let’s go ahead and create a poll.resovlers.js file that will be responsible for resolving (handling) all queries, mutations, and subscriptions related to the poll type:

Moreover, we have to create the poll service, which will be responsible for the business logic implementation. So inside the server/services let’s create poll.service.js:

Finally, we should spread our resolver’s mutation, queries, and subscriptions inside the main resolvers file which is server/resovlers/index.js:

If we go ahead and try the mutation in the playground we should see our poll created:

Vue.js GraphQL Mutation

Querying polls

Next, we will create our first query, which is the polls query. By using it, we will get a list of all the polls we have in the database.

First, we will create the query inside the Query type in the Query.graphql file:

server/schema/Query.graphql

Further, we will add the query resolver to our resolvers file:

And finally, we will implement the findAll() service method:

server/services/poll.service.js

If we go ahead and run this query in the playground we should see an array of polls:

Vue.js GraphQL Query
Click me!

Voting on polls

In this section, we will implement the voting functionality. Each user can vote on the option he likes for a certain poll. In order to implement this, we will create a mutation called voteOnPoll that will accept an option id:

We made the mutation simply return the poll being updated.

Now we will do the usual process. First, implement the resolver:

Then, we will implement the service method:

We have used findOneAndUpdate which will look for the document that contains an option with the provided id. Then, it will increment that specific option votes with one vote. Finally, it will return the whole document in it’s latest state. Let’s give it a shot:

Vue.js GraphQL Voting on Option
Click to expand!

You have to change the optionId in the variables with an id that you already have, use the polls query to get it.

Real-time results using GraphQL subscriptions

In our idea description, we specified that we want to see real-time voting. Furthermore, we want to see newly created polls without refreshing the page.

To implement this kind of business logic, we have to use the apollo-server subscriptions. Where the client-side will establish a WebSocket connection with the server to be updated with new data without sending any requests.

We can define subscriptions inside the Subscription.graphql file, a subscription is very similar to a mutation, it can accept arguments and it must specify a return type. In our case, we will start by defining the pollCreated subscription:

Now inside our resolvers, we have to register this subscription using a new PubSub instance to generate the event we need. Since the subscription will just send the data to the client, we need a way to publish it from anywhere:

If you have noticed, we have used a POLL_CREATED constant, this is because we have used and will continue to use the pollCreated string in multiple places, so it makes sense to save it in an events.js file:

Finally, we will publish data to the pollCreated subscription when we create a new poll inside the createPoll resolver method:

To test this we will head over to the playground:

GraphQL Subscription
click to view fullscreen

Now we will add the second subscription, which is the optionVoted, it will just return the option that has been voted since we don’t really need the whole poll:

And now we will implement the subscription and update the voteOnPoll mutation resolver:

And we’ll also give it a try:

GraphQL Subscription  option voted

Building the Frontend with VueJS

In this section we will start building the front-end part of our app, we will be displaying the polls, voting on polls and adding a form to create new polls.

Fetching the polls

We will be using bootstrap to style our app, luckily we have bootstrap-vue which is an implementation of the bootstrap framework as Vue components. The setup is easy, first, we will install it by running:

> yarn add bootstrap-vue

Then, we will configure it in our main.js file:

We have just imported the styles and registered the BootstrapVue plugin in our Vue app.

After that, we will fetch our data using GraphQL queries, if you take a look at src/gql/person.queries.js you will see that we’re exporting a query using graphql-tag.

graphql-tag will transform our query string into AST, which is required by apollo-client to handle the operation. In our case, we will rename person.queries.js to poll.queries.js, and change the query content:

Further, we have to rename src/views/People.vue to Polls.vue, since this is the view that will render when we load our app.

After that, we need to update and change the import path inside our router.js routing configuration:

Now, if you go to localhost:8080 you will probably find some errors, that’s because our Polls.vue component is still referencing some old files and code. Let’s fix it and use the poll query to fetch our polls.

We will update the imports and supply our imported query to the vue options apollo attribute:

As a result, the response data of the query will be stored inside the polls attribute, which will become reactive data when the component is rendered.

Moreover, if we go ahead and inspect our Polls component using the Vue Devtools we will see that we have our data fetched:

Graphql result fetched inside a vue component
Graphql query went successful, went 200 ok, MUST WATCH!!

Displaying the polls

We will use bootstrap to display the polls in the form of cards in a what’s called a card column group:

And this will result in something like this:

GraphQL result displayed in UI

As you can see, we’re just looping over the polls and creating a card element for each poll, the card element from bootstrap-vue accepts a title, and a sub-title which we used as a description.

Next, inside the body of the card, we’re displaying a list of radio buttons with our options. The value for each radio button is the option’s id, that we will use later for voting, pretty straight forward.

The final step here is to export the card into a separate component which we will call SinglePoll.vue that will live inside src/components:

Then, we will use it inside our Poll.vue component and replace the old code:

Voting for options

To vote for options, we will have two operations to implement, Firstly, we will use the voteOnPoll mutation, which will increment the options’s votes. In addition to that, we will register an optionVoted subscription which will update our data in real-time.

Starting with the mutation, we will first create a src/gql/poll.mutations.js file which will export the GraphQL mutation query:

After that, we will import it into the Polls.vue component and use it whenever we have an optionVote event triggered:

We’re just executing the mutation using Apollo Client which has a reference inside the $apollo attribute of the component. We’re supplying the optionId passed as an argument to the event handler.

The only thing missing now is emitting the event from the single poll component:

When clicking on a radio input, the <b-form-radio-group /> component will emit a change event with the value of the radio input, which in an earlier section we set to be the option’s id.

We’re passing this event up to our Polls.vue component to trigger the handler and execute the mutation.

If we try our app in the current state we should be able to vote on options, However, we can only see the new result after refreshing the page:

Graphql mutation inside a vue app

Subscribing for real-time results

To make our client updates when the option is voted, we will subscribe to the optionVoted subscription. First, we will define the subscription query inside polls.subscriptions.js in src/gql/subscriptions folder:

Next, we will import it into our Polls.vue component and use it inside the polls query:

Let me now explain this new code, so what we did here is that we used the subscription query inside the polls query in a something called subscribeToMore. subscribeToMore, as the name implies, will make apollo-client subscribe to more data coming from the server in real-time.

The thing here is that since the subscribeToMore is attached to a query, when the subscription updated the data in the client, the query result will also be updated since, in our example, a poll and an option are related to each other. In other words, if a poll contains the option being updated, it will also be updated and subsequently, the view will be re-rendered.

Real-time graphql subscription

Creating Polls

Finally, we’re reaching the end of this tutorial, just a few steps until we wrap it up. Now, we will only have to do the poll creation, we will make that “create a poll” button show a poll creation modal, and we will make it as simple as possible.

The modal will show a form that will contain a title text input, a description text area, and a text input for the first option with a button to add more text inputs for additional options:

Our modal will be hidden since we have isModalVisible set to false by default, the only thing that will show up is the Create a poll button, once we click on it the boolean will change to true and our modal will show up.

There, we can write the title, description and add as many options as we want. We have implemented a method that will add an option to the options array with an empty name, and since we can use v-model with array elements, in the template, we’re just looping over the options and doing so.

Once we click on the ok button in the modal we will emit a createPoll event with the poll data.

Now we will use this component in the Polls.vue component and handle the event by running the createPoll mutation:

We’re only missing the CREATE_POLL mutation query, let’s go ahead and add it to poll.mutations.js:

Let’s give it a try:

It works, but it will appear after we reload the page.

Showing real-time created polls

Now, remember I said that this was the last thing we will do at the beginning of this section, Well, I was not quite telling the truth, because the real last thing that we will do, is making this real-time, the poll creation.

First, we will create the subscription query inside polls.subscriptions.js:

Now we will import it inside the Polls.vue component and use it inside the subscribeToMore object, which we will turn into an array of objects since we have two subscriptions now:

So the only difference between the option voted and poll created subscription is that in the latter we have implemented something called updateQuery.

Remember that in an earlier section I said that if the subscription is going to update something that exists in the apollo-cache, Apollo will do it automatically for us, and it will also update all the queries that relate to the entity being updated in the cache.

However, if the subscription is introducing something that doesn’t exist in the cache already, like a new poll in our example, we have to add it manually.

the updateQuery method will provide us with the previousResult, in our case, it will be the list of polls that we already have, and the subscriptionData, which will contain the data of the newly created poll.

Our mission is to merge them together and return the final result. Then, we let Apollo handle the rest.

So what we did here is that we have returned a new array, which will contain the new poll as the first element since we want to see it at the beginning of the list, and then we have spread the list of the older polls.

Let’s give it a try:

A working graphql subscription with updateQuery

Conclusion

I hope that you have learned something from this tutorial. If you have found this useful don’t forget to share and spread the knowledge. See you on the next one!

You can also follow me on:

Share via
Copy link
Powered by Social Snap