GraphQL basics and practical examples with Vue

Vue.js meets GraphQL
Check out my Vue.js 3 course! We cover the Composition API, TypeScript, Unit Testing, Vuex and Vue Router.

Introduction

type Query { // define the query
me: User // define the fields
}
type User { // define the type
id: ID
name: String
}
function Query_me(request) { // define the function
return request.auth.user
}
{
me {
name
}
}
{
“me”: {
“name”: “username”
}
}

Getting Started

npm install -g @vue/cli@latest
vue create graphql-example
mkdir server
npm install express express-graphql graphql --save

Basic Query

const express = require('express')
const { graphql, buildSchema } = require('graphql')
const graphqlHTTP = require('express-graphql')
const cors = require('cors')
  • express and express-graphql will let us response to HTTP requests
  • buildSchema is used to define the types (more soon)
  • cors will let us make requests from our Vue app, which will run on port 8080, to the server running on port 4000
const schema = buildSchema(`
type Query {
language: String
}
`)
const rootValue = {
language: () => 'GraphQL'
}
const app = express()
app.use(cors())
app.use('/graphql', graphqlHTTP({
rootValue, schema, graphiql: true
}))
app.listen(4000, () => console.log('Listening on 4000'))

Making a request

<template>
<div id="app">
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'app'
}
</script>
axios.post('http://localhost:4000/graphql', {
query: '{ language }'
})
<template>
<div id="app">
<h3>Example 1</h3>
<div>
Data: {{ example1 }}
</div>
<button @click="getLanguage">Get Language</button>
<hr>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'app',
data () {
return {
example1: ''
}
},
methods: {
async getLanguage () {
try {
const res = await axios.post(
'http://localhost:4000/graphql', {
query: '{ language }'
})
this.example1 = res.data.data.language
} catch (e) {
console.log('err', e)
}
}
}
}
</script>
An initial demo that returns a String
  • defined a schema
  • created the resolver, rootValue
  • make a request using axios, which included the query

Custom Types with Models

const schema = buildSchema(`
type Query {
language: String
}
type Champion {
name: String
attackDamage: Float
}
`)
class Champion {
constructor(name, attackDamage) {
this.name = name
this.attackDamage = attackDamage
}
}
module.exports = Champion
const schema = buildSchema(`
type Query {
language: String
getChampions: [Champion]
}
type Champion {
name: String
attackDamage: Float
}
`)
const champions = [
new Champion('Ashe', 100),
new Champion('Vayne', 200)
]
const rootValue = {
language: () => 'GraphQL',
getChampions: () => champions
}

Querying for specific fields

{ 
getChampions
}
{
getChampions {
name
}
}
{
"data": {
"getChampions": [
{
"name": "Ashe"
},
{
"name": "Vayne"
}
]
}
}
{
getChampions {
name
attackDamage
}
}
{
"data": {
"getChampions": [
{
"name": "Ashe"
"attackDamage": 100
},
{
"name": "Vayne"
"attackDamage": 200
}
]
}
}
<template>
<div id="app">
<!-- ... -->
<h3>Example 2</h3>
<div>
Data:
<div v-for="champion in champions">
{{ champion }}
</div>
</div>
<button @click="getChampions">Get Champions</button>
</div>
</template>
export default {
name: 'app',
data () {
return {
/* ... */,
champions: []
}
},
methods: {
/* ... */
async getChampions () {
const res = await axios.post(
'http://localhost:4000/graphql', {
query: `{
getChampions {
name
}
}`
})
this.champions = res.data.data
}
}
}
Returning an array of a custom type, Champion

Passing Arguments

  • an additional variables object in the POST body
  • telling the client side query the type of arguments you will parse to the query from variables.
const schema = buildSchema(`
type Query {
language: String
getChampions: [Champion]
getChampionByName(name: String!): Champion
}
type Champion {
name: String
attackDamage: Float
}
`)
const rootValue = {
language: () => 'GraphQL',
getChampions: () => champions, getChampionByName: ({ name }) => {
return champions.find(x => x.name === name)
}
}
async getChampionByName () {
const res = await axios.post('http://localhost:4000/graphql', {
query: `
query GetChampionByName($championName: String!) {
getChampionByName(name: $championName) {
name
attackDamage
}
}`,
variables: {
championName: 'Ashe'
}
})
this.champion = res.data.data.getChampionByName
}
  1. query GetChampionByName is the name we are giving to the query. This can be anything, but should be descriptive of what the query is doing. In this case, since we are only calling getChampionByName, I used a convention when the name is the same as the query on the server side, but capitalized the first letter. In real application, a single API call might include many different operations. Naming the query can make the code more easily understood.
  2. ($championName: String!) means that variables should contain a championName, and it is not optional.
  3. getChampionByName(name: $championName) is the query to execute on the server side. The first argument, name, should use the championName value in the variables object.
  4. We are requesting name and attackDamage in the response.
<template>
<div>
<!-- ... --> <h3>Example 4</h3>
Name: <input v-model="name">
<div>
Data:
{{ champion }}
</div>
<button @click="getChampionByName">Get Champion</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
data () {
return {
/* ... */
champion: {}
}
},
methods: { /* ... */ async getChampionByName () {
const res = await axios.post(
'http://localhost:4000/graphql', {
query: `
query GetChampionByName($championName: String!) {
getChampionByName(name: $championName) {
name
attackDamage
}
}`,
variables: {
championName: 'Ashe'
}
})
this.champion = res.data.data.getChampionByName
}
}
}
A query with arguments

Updating Records

const schema = buildSchema(`
type Query {
language: String
getChampions: [Champion]
getChampionByName(name: String!): Champion
}
type Mutation {
updateAttackDamage(name: String!, attackDamage: Float): Champion
}
type Champion {
name: String
attackDamage: Float
}
`)
const rootValue = {
language: () => 'GraphQL',
getChampions: () => champions, getChampionByName: ({ name }) => {
return champions.find(x => x.name === name)
},
updateAttackDamage: ({ name, attackDamage = 150 }) => {
const champion = champions.find(x => x.name === name)
champion.attackDamage = attackDamage
return champion
}
}
methods: {
/* ... */
async updateAttackDamage () {
const res = await axios.post('http://localhost:4000/graphql', {
query: `
mutation UpdateAttackDamage(
$championName: String!, $attackDamage: Float) {
updateAttackDamage(name: $championName, attackDamage: $attackDamage) {
name
attackDamage
}
}`,
variables: {
championName: this.name,
attackDamage: this.attack
}
})
this.updatedChampion = res.data.data.updateAttackDamage
}
}
<template>
<div>
<!-- ... -->
<h3>Example 4</h3>
Name: <input v-model="name">
Attack Damage: <input v-model.number="attack">
<div>
Data:
{{ updatedChampion }}
</div>
<button @click="updateAttackDamage">Update Champion</button>
</div>
</template>
<script>
import axios from 'axios'
export default { data () {
return {
/* ... */
updatedChampion: {},
attack: 5.5
}
},
methods: {
/* ... */
async updateAttackDamage () {
const res = await axios.post('http://localhost:4000/graphql', {
query: `
mutation UpdateAttackDamage($championName: String!, $attackDamage: Float) {
updateAttackDamage(name: $championName, attackDamage: $attackDamage) {
name
attackDamage
}
}`,
variables: {
championName: this.name,
attackDamage: this.attack
}
})
this.updatedChampion = res.data.data.updateAttackDamage
}
}
}
Updating a record with a mutation
Checking the record was correctly updated

Testing

Conclusion

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store