Getting started with Vue & Apollo GraphQL
This tutorial is targeted at the Vue developer who wants to dive into Vue graphQL. You need a workable knowledge of Vue to follow along.
Table of Content:
- what is graphQL
- What is Apollo
- Setting up graphQL server with node.js and Apollo server
- Installing Vue.js using vue CLI
- Installation and setup of Apollo client
- building a Book listing app with Apollo client and vue.js
Introduction:
REST API’s are a cool and fun way to build endpoints for our server side functionalities.
But as everything else, it also has its down sides too. REST API’s return objects from the back end application.
Most cases, these objects contains more data than what the front end developer needs at the moment.
Example:
user:{
name:'moses west',
email:'dev@email.com',
phone:'0123456789'
}
The front end developer might need just the user’s first name from the object.
user:{
name:'moses west'
}
But at each query, the user object would return everything. This can affect performance greatly in large scale applications. GraphQL provides a solution for us.
What is GraphQL:
GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data.
GraphQL provides a way for your query to return exactly the requested data and nothing more. This is GraphQL’s major advantage.
What is Apollo:
Apollo is a set of tools and libraries developed (by Meteor Development Group) to help developers use GraphQL in their projects. Apollo has a client and a server.
Apollo Server:
Apollo server is a library build to ease the connection of a GraphQL scheme to a HTTP (Hypertext Transfer Protocol) server in Node.js and its supported frameworks.
Apollo Client:
Apollo client is a library build to ease the fetching of data from GraphQL servers with JavaScript.
It is a fully-featured caching GraphQL client. It can be used with any modern JavaScript framework like Vue.js, React, Angular, and more.
What we will be Building:
For the purpose of this tutorial, we will be building a simple Book listing app with Vue.js, Apollo client and a graphQL server.
We will create a simple GraphQL server with Node.js and Apollo server. Let’s get started.
Setting up a GraphQL server with Node.js Apollo server:
make a new directory and navigate into it
mkdir graphql-server
cd graphql-server
We will initialize a new node.js project using NPM
npm init --yes
Your project should contian a package.json file at this point.
Let’s now install dependencies
We need to install two major dependencies Apollo Server and GraphQL. Run the following command to install and save them in your project node_modules directory:
npm install apollo-server graphql
Create an index.js file in your document root folder
touch index.js
We need to define our GraphQL schema in our index.js file
Schema are GraphQl’s way of defining the type of that client can query. We are building a To-Do app. Our Schema should enable us query To-Do items by title and author.
const { ApolloServer, gql } = require('apollo-server');
// A schema is a collection of type definitions (hence "typeDefs")
// that together define the "shape" of queries that are executed against
// your data.
const typeDefs = gql`
# Comments in GraphQL strings (such as this one) start with the hash (#) symbol.
# This "Book" type defines the queryable fields for every book in our data source.
type Book {
title: String
author: String
description: String
}
# The "Query" type is special: it lists all of the available queries that
# clients can execute, along with the return type for each. In this
# case, the "books" query returns an array of zero or more Books (defined above).
type Query {
books: [Book]
}
`;
This snippet defines a simple, valid GraphQL schema. Clients will be able to execute a query named books
, and our server will return an array of zero or more books
Our schema is ready to query Data. Apollo Server can fetch data from any source you connect to (including a database, a REST API, a static object storage service, or even another GraphQL server).
To keep things simple, we will hardcode a set of data in an array. This array will be used to store our data too.
const books = [
{
title: 'Harry Potter and the Chamber of Secrets',
author: 'J.K. Rowling',
description: 'testing shit'
},
{
title: 'Jurassic Park',
author: 'Michael Crichton',
description: 'Shit is getting real'
},
{
title: 'test book',
author: 'Moses west',
description: 'This is a demo graphQL project'
}
];
Define a Resolver:
Resolver’s tell Apollo GraphQl how to fetch data associated with a particular data type. Our resolver would be very simple because our Data is coming form our array. Nothing complex.
Add to following to the bottom of your index.js file.
// Resolvers define the technique for fetching the types defined in the
// schema. This resolver retrieves books from the "books" array above.
const resolvers = {
Query: {
books: () => books,
},
};
Create an instance of ApolloServer
Add the following to the bottom of your index.js file to create an instance of the Apollo Server.
// The ApolloServer constructor requires two parameters: your schema
// definition and your set of resolvers.
const server = new ApolloServer({ typeDefs, resolvers });
// The `listen` method launches a web server.
server.listen().then(({ url }) => {
console.log(`? Server ready at ${url}`);
});
Start your server:
To start your server, run the following from your project root directory.
node index.js
You should see the following
? Server ready at http://localhost:4000/
Create our Vue.js client side:
You need to have Vue CLI installed before running the below command. If you do not have it installed, CLick here to install.
Create a new Vue.js project using Vue CLI:
Now we have our server setup, lets create a new directory and create a new Vue.js project using vue CLI (A command line tool for Vue.js. It provides the ability to quickly scaffold a new project via vue create).
Vue create vue-app
You will be prompted to pick a preset. select “Manually select features” to pick the features you need.
Add Bootstrap for easy styling:
We will be using Bootstrap to style our Vue.js front end. Lets add Bootstrap using the Bootstrap CDN.
Add the following line of code to the head of our index.html
in the public folder of your project.
<!-- bootstrap -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
Add this to the bottom of the body tag
<!-- bootstrap js cdn -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
Creating the Form Field with Bootstrap:
In our newly created Vue app, our App.vue components holds a child component named HelloWorld.vue
. Head over to HelloWorld.vue
remove all unnecessary. Copy and paste the code below to it.
<div class="container">
<div class="col-md-12">
<h1>Book listing APP With Vue.js Apollo and GraphQL</h1>
<form>
<div class="form-group">
<input type="email" class="form-control"
id="exampleFormControlInput1" placeholder="Title">
</div>
<div class="form-group">
<textarea class="form-control"
id="exampleFormControlTextarea1"
rows="3">
</textarea>
</div>
<button type="button" class="btn btn-secondary
btn-lg btn-block">
Block level button
</button>
</form>
</div>
</div>
</div>
At this point, this is what our app should look like.
Install the Apollo Client in Vue.js:
There are two ways of installing Apollo client in Vue.js apps. You can install Apollo using the Vue CLI or you can do it manually.
The following is the Vue CLI command to install Apollo automatically.
vue add apollo
For the purpose of this tutorial, we will install Apollo manually. This helps to better understand what goes on under the hood.
You can install Apollo using ApolloClient or Apollo Boost. Let us proceed with ApolloClient for the purpose of this tutorial.
Use the following command to install ApolloClient
npm install --save vue-apollo graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag
After installing, create an Apollo instance in your app like so:
import { ApolloClient } from 'apollo-client'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
// HTTP connection to the API
const httpLink = createHttpLink({
// You should use an absolute URL here
uri: 'http://localhost:4000//graphql',
})
// Cache implementation
const cache = new InMemoryCache()
// Create the apollo client
const apolloClient = new ApolloClient({
link: httpLink,
cache,
})
In the code-base above, the const httpLink
holds the value of our Apollo server URL. If your Apollo server is on a different port, make sure to change it here also.
Lets install the Apollo client into Vue like so:
import Vue from 'vue'
import VueApollo from 'vue-apollo'
Vue.use(VueApollo)
To be able to use Apollo in all components in the app, lets add Apollo Provider like so:
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
})
Finally add it to our App with the apolloProvider
option like so:
new Vue({
el: '#app',
// inject apolloProvider here like vue-router or vuex
apolloProvider,
render: h => h(App),
})
We are now ready to use Apollo in all our child components in the app.
Using Apollo Client in Vue.js:
Apollo has a special object that is used to trigger it in Vue.js components. In order to use Apollo in our app, we need to use the Apollo special object.
<template>
<div>My component</div>
</template>
<script>
export default {
apollo: {
// Vue-Apollo options here
}
}
</script>
Let us see use our Apollo object in action by making our first query to our Apollo server.
Queries:
For those coming from the REST background, a query in GraphQL is like a GET request to a route or an endpoint. We use a query to fetch data from the server/database.
Lets make our first query to our graphQL server to fetch our To-do lists once the page loads.Head over to our HelloWorld.vue
component and add the following:
import gql from 'graphql-tag'
export default {
name: 'mainApp',
data(){
return{
books:'',
}
},
apollo:{
books:{
query: gql`
query {
books{
title,
author,
description
}
}
`,
}
}
}
Let me explain a few concepts from the code sample above. Firstly, notice how we used the apollo
special object to trigger Apollo in the component. Next is our graphQL query. Notice the syntax.
The books
object in the query should have the same name with the books
variable declared earlier. In the graphQL query, pass in the books
object values to be returned by the graphQL server.
Our query would run once the page has completed loading. Think of it like a mounted hook in Vue.js.
We need to loop through the result of our query. We will do this in the template section of our component and display the result.
<div class="row">
<div class="container mt-4">
<div v-for="book in books" :key="book.id">
<div class="col-md-12">
<div class="">
<div class="card">
<div class="card-body">
Title:{{book.title}}
<hr>
Author:{{book.author}}
Descroption{{book.description}}
</div>
</div>
</div>
</div>
<br>
</div>
</div>
</div>
Our page should look like so:
Great work. We are almost there. Lets handle our form validation and send our Mutation.
Mutations:
Think of mutation as a post
, delete
or put
request in a regular REST API. GraphQL servers do not have many routes like REST API. They contain basically two routes.
The query
and mutation
. This implies that any request that involves changing the state of data in our graphQL server would be sent through mutations.
We send mutations using:
this.$apollo.mutate()
In our app, we need to send or add books to our list of books using mutations like so:
<script>
import gql from 'graphql-tag'
export default {
name: 'mainApp',
data(){
return{
books:'',
title:'',
author:'',
description:''
}
},
apollo:{
books:{
query: gql`
query {
books{
title,
author,
description
}
}
`,
}
},
methods:{
createBook(){
this.$apollo.mutate({
mutation: gql`
mutation createBook($title:String!, $author:String!, $description:String!){
createBook(title: $title, author:$author, description:$description){
title,
author,
description
}
}
`,
variables: {
title: this.title,
author: this.author,
description: this.description
}
})
.then(response => {
this.books = response.data.createBook //adding it to our previous query
location.reload()
})
},
}
}
</script>
We need to modify our graphQL server for this mutation like so:
const { ApolloServer, gql } = require('apollo-server');
// A schema is a collection of type definitions (hence "typeDefs")
// that together define the "shape" of queries that are executed against
// your data.
const typeDefs = gql`
type Book {
title: String
author: String
description: String
}
type Query {
books: [Book]
}
type Mutation {
createBook(title: String, author: String, description: String): Book
}
schema {
query: Query
mutation: Mutation
}
`;
const books = [
{
title: 'Harry Potter and the Chamber of Secrets',
author: 'J.K. Rowling',
description: 'testing shit'
},
{
title: 'Jurassic Park',
author: 'Michael Crichton',
description: 'Shit is getting real'
},
{
title: 'test book',
author: 'Moses west',
description: 'This is a demo graphQL project'
}
];
//
function save ({title, author, description}) {
let item = {title, author, description}
books.unshift(item)
return item
}
// Resolvers define the technique for fetching the types defined in the
// schema. This resolver retrieves books from the "books" array above.
const resolvers = {
Query: {
books: () => books,
},
Mutation: {
async createBook (_, {title, author, description}) {
return await save({title, author, description})
}
}
};
const server = new ApolloServer({ typeDefs, resolvers });
// The `listen` method launches a web server.
server.listen().then(({ url }) => {
console.log(`? Server ready at ${url}`);
});
Here we added a type mutation into graphQL query with a createBook
method of type Book. Also notice how we used the save()
method in the Mutation object. We created the save()
method to add data coming from out apollo client to the data store ( our array in this case).
If everything goes fine, your form should be working and you should be able to add Books to your list.
Lets spice our app up a little by validation the form fields like so:
methods:{
createBook(){
if(this.title != "" && this.author != "" && this.description != ""){
this.$apollo.mutate({
mutation: gql`
mutation createBook($title:String!, $author:String!, $description:String!){
createBook(title: $title, author:$author, description:$description){
title,
author,
description
}
}
`,
variables: {
title: this.title,
author: this.author,
description: this.description
}
})
.then(response => {
this.books = response.data.createBook
location.reload()
})
}else{
alert('please fill all forms')
}
},
}
Here, we modified the createBook
method to validate the form using the if(){}
statement above.
Conclusion:
Every graphQL project must have a graphQL server and a graphQL client. Here we created our server with node.js and Apollo server and we created our client with Apollo client and Vue.js.
Unlike REST API’s, graphQL servers do not have many routes. We fetch data from the graphQL server using our query, we insert, update or delete data in a graphQL server using mutations.
Here we have created a Book listing app using graphQL and Apollo. We have just scratched the surface of what graphQL can do. Check out the documentation for more advanced features of graphQL.
Links to our graphQL server and our apollo client code base.