
Creating a GraphQL Client With the Vue.js Framework

In part 3 of our GraphQL series, its time to put our API into action - this tutorial will guide you through creating a simple GraphQL client web app, all through the use of the Vue.js framework. Contains all the relevant code you need for the project!
Recently, we’ve been introducing you to GraphQL and how it fits into the new wave style of designing APIs. It’s a clever alternative to REST architectures, and works very well for particular projects and use cases. Part one of our series was a run down of GraphQL, how it differs from other technologies, and where to use it, and part two outlined creating an API in Node.js using GraphQL.
In this third part of our series we will focus on building a simple web application using GraphQL for communication - with the API we built in part two.
We will use vue-apollo (powered by apollo-client internally) for integrating the GraphQL library into our Vue.js application.
Our interface will allow us to list projects requiring specific tech skills and show the best candidates for each project based on their knowledge and experience.
This tutorial assumes you already have Node.js and NPM installed and that the API that we built in part two is up and running on port 3200.
Here is a link to our repo with finished project - https://github.com/iRonins/GraphQL-client-with-Vue.js
Creating a new project
We will use the vue-cli tool to bootstrap our project. vue-cli
allows us to easily create a structure and the necessary configuration for the project using one of the available templates.
npm install -g vue-cli
vue init webpack-simple talent-matcher
cd talent-matcher
npm install
To keep this tutorial simple, we will use the webpack-simple template. For real-world applications we would recommend that you use the full-featured webpack template.
The above commands will create an initial file structure and Webpack configuration for our app.
Before we start writing our app, we need to install vue-apollo
and apollo-client
(for connecting to the GraphQL API), bootstrap-vue
(for some nice UI), and some css loaders (for importing Bootstrap styles):
npm install -D vue-apollo bootstrap-vue apollo-client@1.0.1 style-loader css-loader
We also recommend that you install Apollo DevTools to improve your debugging and development experience. It will provide you information about the available queries and mutations within your API, execute them, and preview the current state of the application’s store.
Writing the actual app
We need to start by making our app aware of all the add-ons that we just installed:
src/main.js
// src/main.js
import Vue from 'vue'
import { ApolloClient, createNetworkInterface } from 'apollo-client'
import BootstrapVue from 'bootstrap-vue'
import VueApollo from 'vue-apollo'
import 'bootstrap-vue/dist/bootstrap-vue.css'
import 'bootstrap/dist/css/bootstrap.css'
import App from './App.vue'
const apolloClient = new ApolloClient({
networkInterface: createNetworkInterface({
uri: 'http://localhost:3200/graphql'
}),
connectToDevTools: true
})
const apolloProvider = new VueApollo({
defaultClient: apolloClient
})
Vue.use(VueApollo)
// Fix for compatibility issue with Vue 2.5.1
// https://github.com/bootstrap-vue/bootstrap-vue/issues/1201
let originalVueComponent = Vue.component
Vue.component = function(name, definition) {
if (name === 'bFormCheckboxGroup' || name === 'bCheckboxGroup' ||
name === 'bCheckGroup' || name === 'bFormRadioGroup') {
definition.components = {bFormCheckbox: definition.components[0]}
}
originalVueComponent.apply(this, [name, definition])
}
Vue.use(BootstrapVue)
Vue.component = originalVueComponent
new Vue({
el: '#app',
apolloProvider,
render: h => h(App)
})
We basically import vue-apollo
, apollo-client
, bootstrap-vue
and some Bootstrap styles.
Unfortunately, we also need to override the Vue.component
function while we are importing BoostrapVue
, because there is an incompatibly bug that causes an error (Invalid value for option "components": expected an Object, but got Array
) on the 2.5.x
version of Vue
.
We also need to import style-loader
and css-loader
modules to our Webpack configuration so we can import the Bootstrap styles in our main.js
script:
// webpack.conf.js
...
module.exports = {
...
module: {
rules: [
...,
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{ loader: "css-loader" }
]
}
]
}
}
Now let’s modify our application’s template:
// src/App.vue
<template>
<div id="app">
<header class="header">
<b-navbar toggleable="md" type="light" variant="light">
<b-container>
<b-navbar-brand href="#">Talent Matcher</b-navbar-brand>
</b-container>
</b-navbar>
</header>
<main>
<b-container>
<projects />
</b-container>
</main>
</div>
</template>
<script>
import projects from './components/Projects.vue'
export default {
name: 'app',
components: {
projects
}
}
</script>
<style>
.header {
margin-bottom: 2rem;
}
</style>
This basically creates a “shell” of our app (navbar, container, etc.). Our projects will be displayed by the Projects
component, which will be responsible for 2 things:
- listing all projects
- showing the best candidates for the currently selected project
Let’s start by building the skeleton of our Projects
component:
// src/components/Projects.vue
<template>
<b-row>
<b-col cols="4">
<b-list-group>
<b-list-group-item v-for="project in projects"
:key="project.id"
href="#"
:active="selectedProjectId == project.id" @click="selectProject(project)">
</b-list-group-item>
</b-list-group>
</b-col>
<b-col>
<p v-if="!selectedProjectId">Select project on the sidebar to see its best candidates</p>
</b-col>
</b-row>
</template>
<script>
import gql from 'graphql-tag'
const query = gql`
query projects {
projects {
id,
name
}
}
`
export default {
apollo: {
projects: query
},
data() {
return {
projects: [],
selectedProjectId: null
}
},
methods: {
selectProject(project) {
this.selectedProjectId = project.id
}
}
}
</script>
As you can see, we are importing gql
from graphql-tag
in order to create the projects
query responsible for fetching our projects with their id
and name
attributes. Then we provide that query to the apollo
property. The component will automatically send the query to the GraphQL API and place the results inside its projects
data so we can iterate over them using v-for
directive and display each of them using the list-group-item component.
We have also added some nice tweaks from vue-bootstrap
like highlighting the currently selected projects on the list, etc. to make our app more visually appealing.
When you run the app it should look similar to the screenshot below:
Let’s add to it so that we can display the candidates for selected project. To do that we will need to create a new Candidates
component which we will use in the Projects
component.
// src/components/Candidates.vue
<template>
<b-table bordered hover :items="candidates" :fields="fields">
<template slot="skills" slot-scope="data">
()
</template>
</b-table>
</template>
<script>
import gql from 'graphql-tag'
const query = gql`
query projectCandidates($projectId: Int!) {
candidates(projectId: $projectId) {
id,
fullName,
matchedSkillsNo,
matchedSkills,
experience
}
}
`
export default {
props: ['projectId'],
apollo: {
candidates: {
query,
variables() {
return {
projectId: this.projectId
}
}
}
},
data() {
return {
candidates: [],
fields: [
{
key: 'id',
label: 'Id',
sortable: false
},
{
key: 'fullName',
label: 'Full name'
},
{
key: 'skills',
label: 'Matched skills'
},
{
key: 'experience',
label: 'Matched skills experience',
sortable: true
}
]
}
}
}
</script>
As you can see above, we are defining our query to fetch candidates who match the project requirements based on their skills and experience. Our query is a little bit more complicated than the previous one because the projectId
parameter must be bound to the component’s property.
The bootstrap-vue
library also provides a nice table component which allows us to customize the columns and add simple sorting based on the candidate’s experience.
You can check all available options in the documentation for bootstrap-vue
.
We also need to include our nearly created component in the Projects.vue
component:
// src/components/Projects.vue
<template>
...
<p v-if="!selectedProjectId">Select project on the sidebar to see its best candidates</p>
<candidates v-else :projectId="selectedProjectId" />
...
</template>
<script>
import candidates from './Candidates.vue'
...
export default {
components: {
candidates
}
...
}
</script>
When you run the app it should look similar to the screenshot below:
So far so good but when we refresh the page we are losing the previously selected project - it basically always goes back to its initial state.
Let’s fix that by adding proper routing. We will use vue-router for that, the official routing library for Vue
.
Let’s install the dependency first:
npm install -D vue-router
and make our application aware of it:
// src/main.js
import VueRouter from 'vue-router'
...
import Projects from './components/Projects.vue'
...
const router = new VueRouter({
routes: [
{
name: 'projects',
path: '/:projectId?',
component: Projects
}
]
});
Vue.use(VueRouter)
...
new Vue({
el: '#app',
apolloProvider,
router,
render: h => h(App)
})
We also created our single projects
route which supports the optional projectId
parameter to persist our application’s state between page refreshes.
Now we need to modify our Projects
component to take the projectId
parameter from the $route
and assign it to the selectedProjectId
property which will highlight the correct link on the sidebar and pass the correct selectedProjectId
to the Candidates
component which will fetch the correct data.
// src/components/Projects.vue
<template>
...
<b-list-group-item v-for="project in projects"
:key="project.id"
:to="{ name: 'projects', params: { projectId: project.id } }">
</b-list-group-item>
...
</template>
<script>
...
export default {
components: {
candidates
},
apollo: {
projects: query
},
data() {
return {
projects: [],
}
},
computed: {
selectedProjectId() {
return this.$route.params.projectId
}
}
}
</script>
We added a computed property that returns selectedProjectId
based on the projectId
param in the $route
.
We also bind selectedProjectId
to projectId
when loading, so when a user refreshes the page, our application fetches candidates correctly after refresh. Also the back / forward buttons now work thanks to vue-router
.
Let’s also connect the navbar logo (actually navbar-brand
) with our main page trough the router:
// src/App.vue
<template>
<div id="app">
<header class="header">
<b-navbar toggleable="md" type="light" variant="light">
<b-container>
<b-navbar-brand :to="{ name: 'projects' }">Talent Matcher</b-navbar-brand>
</b-container>
</b-navbar>
</header>
...
</template>
We are linking b-navbar-brand
to our main route (projects
) - but without the optional projectId
parameter - this way we can “reset” the application’s state.
We are almost done but we forgot about one missing bit - we do not yet display the skills required by the selected project. Let’s display them above the candidates table.
// src/components/Projects.vue
<template>
...
<p v-if="!selectedProjectId">Select project on the sidebar to see its best candidates</p>
<div v-else>
<p>
<strong>Required skills:</strong>
</p>
<candidates :projectId="selectedProjectId" />
</div>
</template>
<script>
...
const query = gql`
query projects {
projects {
id,
name,
skills
}
}
`
export default {
...
computed: {
selectedProject() {
return this.projects.find(({ id }) => id === this.selectedProjectId) || {}
},
...
},
...
}
</script>
We’ve added selectedProject
as computed property which will return project
data or an empty object, based on the selectedProjectId
value.
We have also modified our query to ask the API to include the skills
attribute for each project.
Now our complete app should look like on the screenshot below:
Here is the link to the repository containing full code of the application.
GraphQL is just one of the tools that we have at our disposal to help build web apps that are easier to comprehend, more flexible, and are fully featured. If you are interested in learning how this new technology could benefit your next web application, or are interested in changing over your current infrastructure, then email us at iRonin to have a chat!

Experts in software development
We are a 100% remote team of software development experts, providing web & mobile application development and DevOps services for international clients.

