Using VueJS + GraphQL to make Practical Magic
Tags: graphql, vue, craftcms
Published: 2020-02-21 01:48:15

---
title: Using VueJS + GraphQL to make Practical Magic
published: true
date: 2018-08-27 22:29:00 UTC
tags: graphql,vuejs,craftcms
canonical_url: https://nystudio107.com/blog/using-vuejs-graphql-to-make-practical-magic
---

# Using VueJS + GraphQL to make Practical Magic

### Make some prac­ti­cal mag­ic with Vue­JS + GraphQL to solve every­day prob­lems like auto-com­plete search­ing and form sub­mis­sion sav­ing with a head­less Craft CMS server

Andrew Welch / [nystudio107](https://nystudio107.com)

![Vuejs Graphql Craft Cms Magic](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1200x675_crop_center-center_82_line/vuejs-graphql-craft-cms-magic.jpg)

The advance of new tech­nolo­gies can be daunt­ing. We hear about shiny new things like [Vue­JS](http://vuejs.org/) and [GraphQL](http://graphql.org/), but there’s only so much time in the day to learn every new thing that comes along.

So I think a more prac­ti­cal approach is to fig­ure out how these tech­nolo­gies can help us solve the real world prob­lems we face every day.

<aside>
                                This arti­cle presents two prac­ti­cal exam­ples using Vue­JS + GraphQL that will feel like magic
                            </aside>

Here are the two prob­lems we’re going to solve:

- **Auto-com­plete search**  — dynam­i­cal­ly present a list of results as the user types
- **Con­tact form sub­mis­sion sav­ing**  — The abil­i­ty to take form sub­mis­sion data, and save it to a back­end database

So let’s get down to it, and talk about the tool­ing we need to get the job done.

## Tool­ing

Every project needs at least a lit­tle bit of tool­ing; I’ve tried to keep it to a min­i­mum here, so we can focus on the exam­ples. But we still need some.

![Craft Cms Vue Js Graphql Tooling](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1200x675_crop_center-center_82_line/craft-cms-vue-js-graphql-tooling.jpg)

Here’s what we’ll be using for our tool­ing on the frontend:

- **[Vue­JS](http://vuejs.org/)** — a fron­tend JavaScript frame­work that is approach­able, ver­sa­tile, and performant
- **[Axios](http://github.com/axios/axios)** — a JavaScript library for doing <kbd>http</kbd> requests
- **[Boot­strap 4](http://getbootstrap.com/)** — a pop­u­lar CSS frame­work, just so our exam­ples don’t look ugly

For sim­plic­i­ty’s sake, all of these fron­tend resources will just be grabbed from a CDN. I used Boot­strap because much as I love [Tail­wind CSS](http://tailwindcss.com/), I did­n’t want to get dis­tract­ed by util­i­ty-first CSS in the examples.

If you’re not famil­iar with Vue­JS, that’s okay. You could do the same thing with jQuery, vanil­la JS, or what­ev­er you like. It’d just be more work; we’re just using Vue­JS here to make the GraphQL exam­ples eas­i­er to do.

A full expla­na­tion of Vue­JS is beyond the scope of this arti­cle, but you can check out the fol­low­ing resources if you want to learn more:

- [Learn Vue 2: Step By Step](http://laracasts.com/series/learn-vue-2-step-by-step)
- [Spelunk­ing into the Vue­JS Fron­tend JavaScript Framework](http://devmode.fm/episodes/spelunking-into-the-vuejs-frontend-javascript-framework)
- [Using Vue­JS 2.0 with Craft CMS](https://nystudio107.com/blog/using-vuejs-2-0-with-craft-cms)
<aside>
                                Hey, where’s the GraphQL?
                            </aside>

You might be look­ing at the list, and won­der­ing to your­self _​“Hey, where’s the GraphQL?”_ There’s a good rea­son it isn’t list­ed there; GraphQL is a spec­i­fi­ca­tion, not an imple­men­ta­tion. So there’s no JavaScript to include at all!

Here’s what we’ll be using for our tool­ing on the backend:

- **[Craft CMS](http://craftcms.com/)** — a won­der­ful CMS that offers a rich con­tent author­ing experience
- **[CraftQL](http://github.com/markhuot/craftql)** — Mark Huot’s excel­lent plu­g­in sim­ply pro­vides a GraphQL lay­er on top of Craft CMS

The exam­ples will be using [Craft CMS](http://craftcms.com/) as the back­end, but the glo­ry of [JAM­stack](http://jamstack.org/) tech­nolo­gies like Vue­JS + GraphQL is that the back­end does­n’t real­ly mat­ter. You could swap out what­ev­er you want­ed to use on the back­end! We’re using Craft CMS as a ​“head­less” CMS just to serve up our con­tent data via API.

Even if you’re not using Craft CMS, almost every­thing in this arti­cle will apply. So read on!

## Auto-com­plete search

It’s pret­ty com­mon that we might want to pro­vide the abil­i­ty for peo­ple to type in a search field, and have it dynam­i­cal­ly list a series of results.

For this exam­ple, we have a <kbd>blog</kbd> sec­tion in Craft CMS that has some sam­ple data in it. We want to let peo­ple type in a field to find blog entries that match what they are typing.

The end result looks like this on the frontend:

![Auto Complete Search](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_2166x1244_crop_center-center_line/auto-complete-search.png)

At the top we have a Search field, and below it we present a dynam­ic list of match­es to blog entries as they type. Below that is just some debug­ging infor­ma­tion that may help you under­stand what’s going on under the hood.

I’m going to jump around a bit in this expla­na­tion, but the full source will be at the end of the article.

## Vue Instance for Auto-com­plete search

So… how do we accom­plish this? Let’s start with defin­ing the data we need to make this hap­pen, and cre­ate our Vue instance around it.

<aside>
                                With Vue­JS, the <span>DOM</span> is a side-effect of your data, not the oth­er way around
                            </aside>

This is what I love about Vue­JS. You define the data as the source of truth for your appli­ca­tion, and the HTML result is just a byprod­uct of it. 

Let’s have a look:

```

// Instantiate our Vue instance
    new Vue({
        el: '#demo',
        data: {
            searchApi: axios.create(configureApi(apiUrl, apiToken)),
            searchQuery: '',
            searchResults: {}
        },
        methods: {
            // Perform a search
            performSearch() {
                // If they haven't entered anything to search for, return nothing
                if (this.searchQuery === '') {
                    this.searchResults = {};
                    return;
                }
                // Set the variables we will pass in to our query
                const variables = {
                    sections: searchSections,
                    needle: searchPrefix + this.searchQuery,
                    limit: 5
                };
                // Execute the query
                executeQuery(this.searchApi, searchQuery, variables, (data) => {
                    this.searchResults = data.data.entries;
                });
            }
        }
    })

```

Our data is pret­ty sim­ple, and con­sists of just:

- <kbd>searchApi</kbd> — the Axios instance we’ll use to send & receive GraphQL via <kbd>http</kbd> (more on this later)<kbd></kbd>
- <kbd>searchQuery</kbd> — the search string the user is look­ing for
- <kbd>searchResults</kbd> — and object with the results (if any) of their search

The <kbd style="font-style:inherit;">configureApi()</kbd> func­tion looks like this:

```

// Configure the api endpoint
    const configureApi = (url, token) => {
        return {
            baseURL: url,
            headers: {
                'Authorization': `Bearer ${token}`,
                'X-Requested-With': 'XMLHttpRequest'
            }
        };
    };

```

It’s return­ing a con­fig object that we can pass to <kbd>axios.create()</kbd> so that all of our <kbd>http</kbd> requests have the same basic set­tings. We’re just cre­at­ing our own Axios instance that is pre-con­fig­ured with the set­tings we want. 

Here are the set­tings we pass in:

```

// Information needed for connecting to our CraftQL endpoint
    const apiToken = 'wwYfgLejf27AxoSmR0K3wUzFoj9Y96QSNTICvpPslO2l2JcNsjfRY9y5eIec5KhN';
    const apiUrl = '/api';

```

While this might seem over­ly com­pli­cat­ed, what if we had mul­ti­ple API URLs? Or what if we had dif­fer­ent per­mis­sions for each type of API call? This makes it eas­i­er to set up our API end­points in a reusable way.

<kbd>apiUrl</kbd> is set to the default <kbd>/api</kbd> URL that CraftQL lis­tens to for GraphQL requests. <kbd>apiToken</kbd> is a [Bear­er Token](http://swagger.io/docs/specification/authentication/bearer-authentication/) that CraftQL uses to grant per­mis­sion to read and write data in Craft CMS.

In the Craft AdminCP, you cre­ate these bear­er tokens:

![Craftql Bearer Rokens](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1428x604_crop_center-center_line/craftql-bearer-rokens.png)

And define what per­mis­sions they have:

![Craftql Token Scopes](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_2036x1290_crop_center-center_line/craftql-token-scopes.png)

None of this is unique to Craft CMS or CraftQL; what­ev­er you end up using on the back­end, there will be a URL to access the API, and a bear­er token to define permissions.

## HTML for Auto-com­plete search

So that’s our Vue instance; before we get to the <kbd>performSearch()</kbd> method and our GraphQL, let’s have a look at the HTML tem­plate we’re using:

```

<!DOCTYPE html>
<html>
<head>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>

<body>
<div class="container p-5">
    {% verbatim %}
    <form id="demo" autocomplete="off">
        <div class="form-group">
            <label for="searchQuery">Search:</label>
            <input v-model="searchQuery" v-on:keyup="performSearch()" id="searchQuery" class="form-control" type="text" />
        </div>

        <div class="form-group">
            <ul class="list-group">
                <li v-for="(searchResult, index) in searchResults" class="list-group-item">
                    <a v-bind:href="searchResult.url">{{ searchResult.title }}</a>
                </li>
            </ul>
        </div>

        <div class="form-group">
            <pre>data: {{ $data }}</pre>
        </div>
    </form>
    {% endverbatim %}
</div>

```

So noth­ing too excit­ing here; we have our JavaScript and Boot­strap CSS com­ing from CDNs.

Then we have the rather strange look­ing <kbd>`{% verbatim %}`</kbd> state­ment. This is just a [Twig tag](http://twig.symfony.com/) that tells Craft CMS not to process any­thing inside of it. We have to do this because both Twig and Vue­JS uses the same mus­tache <kbd>`{{ }}`</kbd> delim­iters, and we want to be using Vue­JS here, not Twig.

Then we have an input that is bound to our <kbd>searchQuery</kbd> data in Vue­JS via the <kbd>v-model</kbd> attribute. This means that any time the val­ue of the <kbd>searchQuery</kbd> data changes, so does our input… and vice ver­sa, any time the user types some­thing into the input, the val­ue in our <kbd>searchQuery</kbd> data is updated.

There is also a <kbd>v-on:keyup</kbd> attribute set on it that caus­es Vue­JS to call the <kbd>performSearch()</kbd> method any time there’s a <kbd>keyup</kbd> event. This is what caus­es our API call to GraphQL to hap­pen dynam­i­cal­ly as the user types.

<aside>
                                This is the reac­tive mag­ic of Vue­JS at work
                            </aside>

After that we have a list item that has the <kbd>v-for</kbd> attribute set on it. This caus­es Vue­JS to ren­der a <kbd>&lt;li&gt;</kbd> for every object in our <kbd>searchResults</kbd> data.

So when­ev­er data is returned from our GraphQL API end­point, the <kbd>searchResults</kbd> data gets updat­ed, which caus­es the DOM on the fron­tend to mag­i­cal­ly update with all of the results.

If there are no results, then noth­ings renders!

The <kbd>`{{ $data }}`</kbd> at the bot­tom just dumps all of the data in our Vue instance as JSON, so we can see what’s going on under the hood.

## GraphQL for Auto-com­plete Search

Now let’s have a look in more detail at our <kbd>performSearch()</kbd> method. While this is tech­ni­cal­ly still part of our Vue instance, it’s rel­e­vant to the GraphQL query we’ll be doing:

```

// Perform a search
            performSearch() {
                // If they haven't entered anything to search for, return nothing
                if (this.searchQuery === '') {
                    this.searchResults = {};
                    return;
                }
                // Set the variables we will pass in to our query
                const variables = {
                    sections: searchSections,
                    needle: searchPrefix + this.searchQuery,
                    limit: 5
                };
                // Execute the query
                executeQuery(this.searchApi, searchQuery, variables, (data) => {
                    this.searchResults = data.data.entries;
                });
            }

```

First it just checks to see if the <kbd>searchQuery</kbd> is an emp­ty string, and if so sets <kbd>searchResults</kbd> to an emp­ty object, and returns.

We do this because if we pass an emp­ty search string into our Craft CMS back­end, it’s going to return _all_ results. We want it to return _none_.

Then it sets the <kbd>variables</kbd> we’re going to pass in to our GraphQL query. If you’re famil­iar with Craft CMS, this should seem fair­ly sim­i­lar to what we might pass in to <kbd>craft.entries</kbd> to look up data:

- <kbd>sections</kbd> — the Sec­tions to search in Craft CMS
- <kbd>needle</kbd> — the search string to look for; this is what­ev­er the user typed pre­fixed with <kbd>searchPrefix</kbd>
- <kbd>limit</kbd> — the num­ber of results we want returned

To make things easy to change, we’ve defined the fol­low­ing constants:

```

// What to search for
    const searchSections = ['blog'];
    const searchPrefix = 'title:';

```

The <kbd>searchSections</kbd> tells it we only want to search the <kbd>blog</kbd> sec­tion. The <kbd>searchPrefix</kbd> is used to lim­it the search to just the <kbd>title</kbd> field, and it all works just the same as [Search­ing in Craft CMS](http://docs.craftcms.com/v3/searching.html#supported-syntaxes). If we want­ed it to search _every­thing_ in an entry, we could just set this to be an emp­ty string (<kbd>''</kbd>).

Final­ly we get to some GraphQL! Next it calls <kbd>executeQuery()</kbd>, pass­ing in our Axiois API we cre­at­ed, the query we want to exe­cute, our <kbd>variables</kbd>, and then a call­back function.

Here’s what the <kbd>searchQuery</kbd> GraphQL query looks like:

```

// The query to search for entries in Craft
    const searchQuery =
        `
        query searchQuery($sections: [SectionsEnum], $needle: String!, $limit: Int)
        {
            entries(section: $sections, search: $needle, limit: $limit) {
                title
                url
            }
        }
        `;

```

While the syn­tax may look a lit­tle funky to you, it should be pret­ty clear what’s going on here. We’re defin­ing a GraphQL query called <kbd>searchQuery</kbd> and we’re defin­ing the names of the incom­ing vari­ables _as well as their type_. The <kbd>!</kbd> after a type def­i­n­i­tion means that the vari­able is _required,_ and <kbd>[]</kbd> is array syn­tax in GraphQL.

This is an impor­tant con­cept in GraphQL; it has a strict type sys­tem to ensure the puri­ty & cor­rect­ness of the data being passed into it. See the GraphQL doc­u­men­ta­tion on [Schemas & Types](http://graphql.org/learn/schema/) for more infor­ma­tion, if you’re curious.

<aside>
                                You must tell GraphQL not only what vari­ables you’re pass­ing in, but what type of data they expect
                            </aside>

GraphQL uses the query we pass in along with the vari­ables to deter­mine what data to select. Then the <kbd>title</kbd> and <kbd>url</kbd> are telling GraphQL what data we want back.

This is anoth­er impor­tant con­cept in GraphQL: it will only return to you the data you ask for! So even though these blog entries may con­tain a huge amount of data, it’s only going to return to us the <kbd>title</kbd> and <kbd>url</kbd> that we’re ask­ing for.

<aside>
                                GraphQL returns only what you ask for, which means it can be super lightweight
                            </aside>

Even if the syn­tax of the query does­n’t make 100% sense to you, that’s okay. You can see that it’s send­ing in some data to look for in the query, and defin­ing what it’s returning.

When the query is com­plete, it will call our call­back function:

```

(data) => {
    this.searchResults = data.data.entries;
}

```

It only calls our call­back if the result­ing query is suc­cess­ful; and we just set our <kbd>searchResults</kbd> to a sub­set of the <kbd>data</kbd> (just the entries) that was returned.

So good enough, let’s look at the guts of the <kbd>executeQuery()</kbd> func­tion to see what exact­ly it’s doing:

```

// Execute a GraphQL query by sending an XHR to our api endpoint
    const executeQuery = (api, query, variables, callback) => {
        api.post('', {
            query: query,
            variables: variables
        }).then((result) => {
            if (callback) {
                callback(result.data);
            }
            console.log(result.data);
        }).catch((error) => {
            console.log(error);
        })
    };

```

It’s actu­al­ly real­ly sim­ple! We’re not using any heavy GraphQL-spe­cif­ic JavaScript, we’re just using our Axios instance that we cre­at­ed to send a <kbd>POST</kbd> to our API URL with our data!

The first para­me­ter to the <kbd>.post()</kbd> method is the URL which gets append­ed to the <kbd>baseURL</kbd> we spec­i­fied ear­li­er when we cre­at­ed our Axios instance. Since we’re just using one URL for all of our API, we pass in an emp­ty string (<kbd>''</kbd>).

The sec­ond para­me­ter to the <kbd>.post()</kbd> method is the data object we want to <kbd>POST</kbd> to the API end­point; all we need here is the <kbd>query</kbd> and <kbd>variables</kbd> for our GraphQL query.

Then since the <kbd>.post()</kbd> method returns a [Promise](http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), <kbd>then</kbd> we call our <kbd>callback</kbd> when the data suc­cess­ful­ly returns, or we <kbd>catch</kbd> any errors, and log them to the console.

## Have a Beer!

Phew! Are you tired? I’m tired! But I think the actu­al con­cepts here are not so bad, there is just some new nomen­cla­ture to learn.

![Have A Beer](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1200x675_crop_center-center_82_line/have-a-beer.jpg)

We cov­ered most of the impor­tant con­cepts that you need to under­stand how every­thing works already, so have a beer to cel­e­brate, then let’s dive in to Con­tact form sub­mis­sion saving.

It won’t be that bad, since the major­i­ty of it is the same!

## Con­tact form sub­mis­sion saving

Anoth­er com­mon thing that needs doing is the user enters some data on the fron­tend, and you want to save it on the back­end in a database.

In our case, we want to save peo­ple’s name, email address, and mes­sage from a con­tact form into our data­base on the back­end so that our CRM folks can get back in touch with them.

On the fron­tend, it looks like this:

![Contact Form Submission Saving](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_2166x973_crop_center-center_line/contact-form-submission-saving.png)

So, pret­ty stan­dard. The user fills in a Name, Email, and Mes­sage, then clicks on the **Sub­mit** but­ton… and we save the infor­ma­tion in the data­base on the backend.

We also dis­play a nice lit­tle mes­sage to the user telling them that the sub­mis­sion was suc­cess­ful­ly sub­mit­ted. It’s the lit­tle things.

## Vue Instance for Con­tact form sub­mis­sion saving

Our Vue instance for the con­tact form is going to look pret­ty familiar:

```

// Instantiate our Vue instance
    new Vue({
        el: '#demo',
        data: {
            contactApi: axios.create(configureApi(apiUrl, apiToken)),
            contactName: '',
            contactEmail: '',
            contactMessage: '',
            submitted: false
        },
        methods: {
            // Submit the contact form
            submitContactForm() {
                // Set the variables we will pass in to our mutation
                const variables = {
                    contactName: this.contactName,
                    contactEmail: this.contactEmail,
                    contactMessage: this.contactMessage,
                };
                // Execute the query
                executeQuery(this.contactApi, contactFormMutation, variables, (data) => {
                    this.submitted = true;
                });
            }
        }
    })

```

We have our data as follows:

- <kbd>contactApi</kbd> — the Axios instance we’ll use to send & receive GraphQL via <kbd>http</kbd>
- <kbd>contactName</kbd> — the name the user enters into the con­tact form
- <kbd>contactEmail</kbd> — the email address the user enters into the con­tact form
- <kbd>contactMessage</kbd> — the mes­sage the user enters into the con­tact form
- <kbd>submitted</kbd> — whether or not the con­tact form was suc­cess­ful­ly submitted

The <kbd>configureApi()</kbd> func­tion looks… well, dang, it’s exact­ly the same as we used in on Auto-com­plete Search exam­ple. Yay, code re-use!

The only thing that is dif­fer­ent are the set­tings we pass in, because we have a sep­a­rate bear­er token for the con­tact form that has per­mis­sions that allow it to save data to our Con­tact Form channel:

```

// Information needed for connecting to our CraftQL endpoint
    const apiToken = 'DxOES1XTDtnFVILEp0kNcOpvJpRXOmjFQci4lz6jLrrUqan6zTJ02ZkZyM_VTXlH';
    const apiUrl = '/api';

```

This is great, it’s lever­ag­ing every­thing we’ve done already, so let’s move right along to the HTML for the con­tact form!

## HTML for Con­tact form sub­mis­sion saving

Before we get into what the <kbd>submitContactForm()</kbd> method does, let’s have a look at the HTML tem­plate for our con­tact form:

```

<!DOCTYPE html>
<html>
<head>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>

<body>
<div class="container p-5">
    {% verbatim %}
    <form id="demo" autocomplete="off">
        <div class="form-group">
            <label for="contactName">Name:</label>
            <input v-model="contactName" id="contactName" class="form-control" type="text" />
        </div>

        <div class="form-group">
            <label for="contactEmail">Email:</label>
            <input v-model="contactEmail" id="contactEmail" class="form-control" type="text" />
        </div>

        <div class="form-group">
            <label for="contactMessage">Message:</label>
            <textarea v-model="contactMessage" id="contactMessage" class="form-control"></textarea>
        </div>

        <div class="form-group">
            <button v-on:click="submitContactForm()" type="button" class="btn btn-primary">Submit</button>
        </div>

        <div v-if="submitted" class="alert alert-primary" role="alert">
            Message submitted!
        </div>

        <div class="form-group">
            <pre>data: {{ $data }}</pre>
        </div>
    </form>
    {% endverbatim %}
</div>

```

Again we have the same JavaScripts and Boot­strap CSS at the top, and then we have a pret­ty stan­dard look­ing con­tact form HTML, with inputs for each piece of data that we want.

We again use the <kbd>v-model</kbd> attribute to bind the inputs to the appro­pri­ate data in our Vue instance, so we get that love­ly reac­tiv­i­ty when­ev­er data is entered.

Then we have a but­ton with the <kbd>v-on:click</kbd> attribute set, so that it’ll call our <kbd>submitContactForm()</kbd> method when­ev­er the user clicks on the button.

Final­ly, we have a <kbd>&lt;div&gt;</kbd> with the <kbd>v-if</kbd> attribute set to dis­play only if <kbd>submitted</kbd> is <kbd>true</kbd>, to dis­play a nice mes­sage to the user to let them know their sub­mis­sion worked. _Because we care._

## GraphQL for Con­tact form sub­mis­sion saving

Now let’s get back to our <kbd>submitContactForm()</kbd> method to see what it’s doing:

```

// Submit the contact form
            submitContactForm() {
                // Set the variables we will pass in to our mutation
                const variables = {
                    contactName: this.contactName,
                    contactEmail: this.contactEmail,
                    contactMessage: this.contactMessage,
                };
                // Execute the query
                executeQuery(this.contactApi, contactFormMutation, variables, (data) => {
                    this.submitted = true;
                });
            }

```

So pret­ty sim­ple, we’re extract­ing out the <kbd>variables</kbd> we want to pass along to GraphQL, and we’re call­ing <kbd>executeQuery()</kbd> again to exe­cute our query.

The nifty thing here is that <kbd>executeQuery()</kbd> is once again exact­ly the same code! Even though we’re doing some­thing dif­fer­ent (sav­ing data instead of read­ing data), we can use the exact same <kbd>executeQuery()</kbd> method!

<aside>
                                Every­thing in GraphQL is a query
                            </aside>

When we want to change or add new data in GraphQL, that’s called a  **muta­tion**. Muta­tions are just anoth­er query that _hap­pen to also change or add data_.

So here’s what our <kbd>contactFormMutation</kbd> looks like:

```

// The mutation to write contact form data to Craft
    const contactFormMutation =
        `
        mutation contactFormMutation($contactName: String!, $contactEmail: String!, $contactMessage: String!)
        {
            upsertContactForm(
                authorId: 1
                title: $contactName
                contactName: $contactName
                contactEmail: $contactEmail
                contactMessage: $contactMessage
            ) {
            id
            }
        }
        `;

```

So it looks pret­ty sim­i­lar to what we were doing before, but instead of <kbd>query</kbd> it’s now <kbd>mutation</kbd>. We’re still telling GraphQL what vari­ables we’re pass­ing in, and also the types of those variables.

But we’ve added <kbd>upsertContactForm()</kbd> that has a list of the data we want to _upsert_ into the data­base. Upsert just means ​“add or update data,” and the Con­tact­Form part is the name of the Sec­tion we want to upsert into.

Then since a muta­tion is just a type of query, we have to tell GraphQL what data we want returned; in this case we just ask for the <kbd>id</kbd> of the new­ly cre­at­ed entry back.

The fields we’re upsert’ing into the Con­tact Form chan­nel match what we have defined in Craft CMS:

![Contact Form Craft Cms Fields](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1650x914_crop_center-center_line/contact-form-craft-cms-fields.png)

The only thing slight­ly unusu­al about this is what we’re pass­ing in a hard-cod­ed <kbd>authorId</kbd>; this is because all Entries need to be owned by some­one in Craft CMS.

That’s it! We’re sav­ing entries in the Craft CMS backend.

Obvi­ous­ly there’s more we could do here, such as val­i­dat­ing the form input with [vee-val­i­date](http://github.com/baianat/vee-validate), hid­ing the form after it’s been sub­mit­ted, etc. But that’s left as an exer­cise for you, dear reader.

## Wrap­ping Up

While this may seem like a good bit to take in, once you get famil­iar with how GraphQL works, it’s infi­nite­ly eas­i­er to use than ​“rolling your own” cus­tom API with the [Ele­ment API](http://github.com/craftcms/element-api), and you’ll have learned a skill that trans­lates to many dif­fer­ent platforms.

![Victory Image](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_1200x675_crop_center-center_82_line/victory-image.jpg)

The best part is… you’ve sep­a­rat­ed your API from the sys­tem that imple­ments it. So if you decide to move to a dif­fer­ent CMS or plat­form, it makes it infi­nite­ly eas­i­er to do so!

One of the most fun and enjoy­able ways you can learn GraphQL is by sim­ply play­ing around with the in-brows­er [GraphiQL](http://github.com/graphql/graphiql) IDE that is includ­ed with the [CraftQL plu­g­in](http://github.com/markhuot/craftql):

![Graphiql Ide Explorer Auto Complete](https://nystudio107-ems2qegf7x6qiqq.netdna-ssl.com/img/blog/_2396x1648_crop_center-center_line/graphiql-ide-explorer-auto-complete.png)

You can play around with your queries & muta­tions with an auto-com­plete edi­tor that knows the schema of your entry Craft CMS back­end. It’s so fun!

If you just can’t get enough GraphQL, the [GraphQL basics and prac­ti­cal exam­ples with Vue](https://medium.com/@lachlanmiller_52885/graphql-basics-and-practical-examples-with-vue-6b649b9685e0) arti­cle is a great place to go next. Also check out the [GraphQL: Bet­ter than all the REST?](http://devmode.fm/episodes/graphql-better-than-all-the-rest) pod­cast on [dev​Mode​.fm](http://devmode.fm/)!

Enjoy your day!

## Auto-com­plete search full source

Here’s the full source to the Auto-com­plete search example:

```

<!DOCTYPE html>
<html>
<head>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>

<body>
<div class="container p-5">
    {% verbatim %}
    <form id="demo" autocomplete="off">
        <div class="form-group">
            <label for="searchQuery">Search:</label>
            <input v-model="searchQuery" v-on:keyup="performSearch()" id="searchQuery" class="form-control" type="text" />
        </div>

        <div class="form-group">
            <ul class="list-group">
                <li v-for="(searchResult, index) in searchResults" class="list-group-item">
                    <a v-bind:href="searchResult.url">{{ searchResult.title }}</a>
                </li>
            </ul>
        </div>

        <div class="form-group">
            <pre>data: {{ $data }}</pre>
        </div>
    </form>
    {% endverbatim %}
</div>

<script>
    // Information needed for connecting to our CraftQL endpoint
    const apiToken = 'wwYfgLejf27AxoSmR0K3wUzFoj9Y96QSNTICvpPslO2l2JcNsjfRY9y5eIec5KhN';
    const apiUrl = '/api';
    // What to search for
    const searchSections = ['blog'];
    const searchPrefix = 'title:';
    // The query to search for entries in Craft
    const searchQuery =
        `
        query searchQuery($sections: [SectionsEnum], $needle: String!, $limit: Int)
        {
            entries(section: $sections, search: $needle, limit: $limit) {
                title
                url
            }
        }
        `;
    // Configure the api endpoint
    const configureApi = (url, token) => {
        return {
            baseURL: url,
            headers: {
                'Authorization': `Bearer ${token}`,
                'X-Requested-With': 'XMLHttpRequest'
            }
        };
    };
    // Execute a GraphQL query by sending an XHR to our api endpoint
    const executeQuery = (api, query, variables, callback) => {
        api.post('', {
            query: query,
            variables: variables
        }).then((result) => {
            if (callback) {
                callback(result.data);
            }
            console.log(result.data);
        }).catch((error) => {
            console.log(error);
        })
    };
    // Instantiate our Vue instance
    new Vue({
        el: '#demo',
        data: {
            searchApi: axios.create(configureApi(apiUrl, apiToken)),
            searchQuery: '',
            searchResults: {}
        },
        methods: {
            // Perform a search
            performSearch() {
                // If they haven't entered anything to search for, return nothing
                if (this.searchQuery === '') {
                    this.searchResults = {};
                    return;
                }
                // Set the variables we will pass in to our query
                const variables = {
                    sections: searchSections,
                    needle: searchPrefix + this.searchQuery,
                    limit: 5
                };
                // Execute the query
                executeQuery(this.searchApi, searchQuery, variables, (data) => {
                    this.searchResults = data.data.entries;
                });
            }
        }
    })
</script>
</body>
</html>

```

## Con­tact form sub­mis­sion sav­ing full source

Here’s the full source for the Con­tact Form Sub­mis­sion Saving:

```

<!DOCTYPE html>
<html>
<head>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>

<body>
<div class="container p-5">
    {% verbatim %}
    <form id="demo" autocomplete="off">
        <div class="form-group">
            <label for="contactName">Name:</label>
            <input v-model="contactName" id="contactName" class="form-control" type="text" />
        </div>

        <div class="form-group">
            <label for="contactEmail">Email:</label>
            <input v-model="contactEmail" id="contactEmail" class="form-control" type="text" />
        </div>

        <div class="form-group">
            <label for="contactMessage">Message:</label>
            <textarea v-model="contactMessage" id="contactMessage" class="form-control"></textarea>
        </div>

        <div class="form-group">
            <button v-on:click="submitContactForm()" type="button" class="btn btn-primary">Submit</button>
        </div>

        <div v-if="submitted" class="alert alert-primary" role="alert">
            Message submitted!
        </div>

        <div class="form-group">
            <pre>data: {{ $data }}</pre>
        </div>
    </form>
    {% endverbatim %}
</div>

<script>
    // Information needed for connecting to our CraftQL endpoint
    const apiToken = 'DxOES1XTDtnFVILEp0kNcOpvJpRXOmjFQci4lz6jLrrUqan6zTJ02ZkZyM_VTXlH';
    const apiUrl = '/api';
    // The mutation to write contact form data to Craft
    const contactFormMutation =
        `
        mutation contactFormMutation($contactName: String!, $contactEmail: String!, $contactMessage: String!)
        {
            upsertContactForm(
                authorId: 1
                title: $contactName
                contactName: $contactName
                contactEmail: $contactEmail
                contactMessage: $contactMessage
            ) {
            id
            }
        }
        `;
    // Configure the api endpoint
    const configureApi = (url, token) => {
        return {
            baseURL: url,
            headers: {
                'Authorization': `Bearer ${token}`,
                'X-Requested-With': 'XMLHttpRequest'
            }
        };
    };
    // Execute a GraphQL query by sending an XHR to our api endpoint
    const executeQuery = (api, query, variables, callback) => {
        api.post('', {
            query: query,
            variables: variables
        }).then((result) => {
            if (callback) {
                callback(result.data);
            }
            console.log(result.data);
        }).catch((error) => {
            console.log(error);
        })
    };
    // Instantiate our Vue instance
    new Vue({
        el: '#demo',
        data: {
            contactApi: axios.create(configureApi(apiUrl, apiToken)),
            contactName: '',
            contactEmail: '',
            contactMessage: '',
            submitted: false
        },
        methods: {
            // Submit the contact form
            submitContactForm() {
                // Set the variables we will pass in to our mutation
                const variables = {
                    contactName: this.contactName,
                    contactEmail: this.contactEmail,
                    contactMessage: this.contactMessage,
                };
                // Execute the query
                executeQuery(this.contactApi, contactFormMutation, variables, (data) => {
                    this.submitted = true;
                });
            }
        }
    })
</script>
</body>
</html>

```

## Further Reading

_If you want to be notified about new articles, follow [nystudio107](https://twitter.com/nystudio107) on Twitter._

<small>Copyright ©2020 nystudio107. Designed by nystudio107</small>