Deploy a fully functional full-stack application from scratch. Part 3 of 4.

Siegfried Grimbeek
Sep 19 · 10 min read
Image credit: Designed by Dooder

In this tutorial, we will connect a GraphQL API with a decoupled Vue.js front-end.


Introduction

Vue.js is a progressive JavaScript framework allowing anyone with HTML, CSS, and JavaScript knowledge to quickly bootstrap an application.

Vue.js is said to have taken the best parts from Angular and React and then improved on them to create Vue.js. I personally find Vue.js really intuitive to work with, for both back- and front-end developers.

Here is a great blog post going into more detail about Vue.js, and if you are brand new to Vue.js, be sure to watch the video below:


Prerequisites

If you have completed the second part of this series, you should be up to speed with the required JavaScript knowledge needed to complete this tutorial.

HTML and CSS knowledge is good to have, but we won’t go too much into it — we will focus more on the JavaScript and Vue.js parts. Be sure to familiarize yourself with the Vue.js templating syntax.

To follow along, you will need to complete Part 2 of this series or clone the repository from Git. I would highly recommend at least skimming through Part 2 to get an overall idea of what data we are working with.


Let’s Get Started

git clone https://github.com/siegfriedgrimbeek/fastify-graphql-api.git
cd fastify-graphql-api
npm i

Feel free to rename the folder to something more intuitive.

The first thing that we want to do is to separate our project files into the back-end (server) and the front-end (client).

To do this, in the project root directory, create two directories — namely aserver directory and aclient directory. Once these are created, copy all the existing code to the server folder.

Before we initialize the Vue.js front-end in the client directory, we need to give our client permission to interact with the server, which can be done by enabling cross-origin resource sharing (CORS).


Enabling Cross-Origin Resource Sharing (CORS)

Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional HTTP headers to tell a browser to let a web application running at one origin (domain) have permission to access selected resources from a server at a different origin. A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) than its own origin.

In the server directory, install the fastify-cors npm package with the following command:

npm i fastify-cors

Once the package has finished installing, add the followings to the server.js file in the src folder:

...// Enable the fastify CORS plugin
fastify.register(require('fastify-cors'), {
origin: '*',
credentials: true
})
...

We now allow all origins to connect to our API. In a production application, we will specify exactly who is allowed to connect.

For testing purposes, make sure to have an instance of the back-end application running, which can be done by navigating to the server directory and starting up the application:

npm run start

Now in another terminal window, navigate to the client directory. Now we will get started with some Vue.js.


Initialize and Set Up a New Vue.js Project

We will be using the Vue CLI tool to create the project. In the client directory, run the following:

npm install -g @vue/cli

This will install the Vue CLI tool as a global npm package. Note that you may need permissions to install a package, so be sure to run the command with sudo for Mac and as an administrator for Windows.

Once the installation is complete, run the following command:

vue create .

This generates the application inside the client directory. Be sure to follow the setup steps listed below:

Choose to install the application in the current directory, and then choose to manually select features option and select the following:

Do not select history mode for the router, and select Sass/SCSS (with node-sass) as the CSS pre-processor.

If you selected the Linter / Formatter option, feel free to customize it to suit your development needs.

Lastly, select the option to store the settings in a dedicated config file and save the settings as a preset with the name fastify-app. This will allow you to use it again at a later stage.

Your Vue.js app is now installing. Once it is done, install the following additional NPM packages:

npm i apollo-boost bootstrap-vue vue-apollo vue-moment graphql

apollo-boost

Apollo Boost is a zero-config way to start using Apollo Client. It includes some sensible defaults, such as our recommended InMemoryCache and HttpLink, which come configured for you with our recommended settings.

Apollo Client is a fully-featured caching GraphQL client with integrations for React, Angular, and more. It allows you to easily build UI components that fetch data via GraphQL. To get the most value out of apollo-client, you should use it with one of its view layer integrations.

vue-apollo

Integrate GraphQL in your Vue.js apps!

boot-strap-vue

With BootstrapVue you can build responsive, mobile-first projects on the web using Vue.js and the world’s most popular front-end CSS library — Bootstrap v4.

vue-moment

Handy Moment.js filters for your Vue.js project. Parse, validate, manipulate, and display dates and times in JavaScript.

Time for a sanity check. Let’s run the application:

npm run serve

Your application should now be running on http://localhost:8080/.


Application Overview

All the application-development files are in the src directory, which gets built to the dist directory within the src directory. The structure is as follows:

In the views directory, there are three main views:

  • Home.vue
  • Car.vue*
  • Owner.vue*

*You can create these files in the views directory.

In thecomponents directory, there are three reusable components:

  • Header.vue*
  • CarCard.vue*
  • EditCar.vue*

*You can create these files in the components directory.

We also have some supporting files used for routing (router.js), state management (store.js), and setup (main.js) files, which have already been created by the Vue CLI tool.


Let's Write Some Code

The first thing that we want to do is to navigate to the src folder and update the main.js file with the following code:

main.js

In the above code, we import several dependencies, initialize the libraries, create the Apollo client and provider, and create the Vue instance.

The above links are for the generic Apollo documentation but are applicable across frameworks. The Vue Apollo documentation is available here.

It is also helpful to learn more about the Vue instance, especially the lifecycle hooks, which are explained very well in this video.

Next, we add the following code to the router.js file:

router.js

Above we just import the external dependencies and the view files. Then we initialize the router and set up the routes.

The routes objects consist of the path, a name, the component (view), and whether or not it gets passed any props.

The props are passed with the following syntax:

:id

Vue Router is the official router for Vue.js. It deeply integrates with Vue.js core to make building Single Page Applications with Vue.js a breeze.

Learn more about routing and the Vue Router here.

Let's update the store.js file with the following code:

store.js

In the above code, we import the external dependencies and initialize Vuex. We then set up the Vuex store, wherein we specify the initial application state as well the mutations which we expect.

Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion. It also integrates with Vue’s official devtools extension to provide advanced features such as zero-config time-travel debugging and state snapshot export / import.

Here is a great article diving deeper into Vuex.

Now lets update theApp.vue file by replacing the following code:

App.vue

Vue Router will load the same Home.vue as the default page because we specified the home view as the default(/) route.

We load the router-view into our app with the following code:

<router-view />

We then declare and run the CarsQuery that will return all the cars in the database and then commit the result to the Vuex store.

Note that the CarsQuery is executed before the component mounts, using the beforeMount() lifecycle hook.

Feel free to log the response variable to the console or to use the Vue dev-tools extension to see the response in our Vuex store.


Creating the Views and Components

Firstly, we can add the code below to the Header.vue component:

Header.vue

With the above code, we create a simple reusable Header component with the string props title and intro. The header component is built using the Bootstrap Jumbotron Component.

Next, we can populate the CarCard.vue component:

CarCard.vue

Here we use the Bootstrap Card Component with the Car object as a prop. The car details and its properties are displayed using the Vue template syntax, and it has two click events that use the Vue Router to navigate to the individual Car view or to the Owner view, which we will implement next.

We use the Lorem Flickr API to serve images related to the car using the following code:

:img-src="`https://loremflickr.com/350/200/${car.brand},${car.title}`"

We need to specify the API URL, the image width, and the image height. Then we use template literals to specify the car brand and car title, which come from the car object.

The two click events, viewCar(id) and viewOwner(id), use the Vue Router to navigate to their respective pages, but before they will work, we will have to populate those views first.

Lastly, there are some CSS styling rules which we will not go into.

Now that we have the two components that will be used by Home.vue in place, we can update the Home.vue with the following:

Home.vue

In the code above, we are making use of the two components that we had created earlier.

For the Header component, we bind the following props:

v-bind:title="'My Car Garage'"
v-bind:intro="'A MongoDB, fastify and GraphQL powered Vue.Js app'"

For the CarCard component, we loop over the carData array (retrieved from the Vuex store) using the list-rendering function. Here we bind the car object as a prop and provide the car.id as the key.

You need to provide a unique key attribute for each item so Vue can track each item’s identity. Learn more about the key attribute here.

In the script tag, we import the dependencies and components. Then we use the mapState helper to generate a computed getter function for us, loading the carData into the state. Learn more about the mapState helper here.


Sanity Check

If everything went well, we should see the cars loaded on the homepage:

Home Page

As stated above, the @click events do work as they are redirecting to the correct views, but at the moment, these views are empty, so let’s populate them.


Car View

In the views directory and in the Car.vue file, add the following code:

Car.vue

Let’s break down each section piece for piece, starting with the <template> tag.

As with our other views and components, we use the Vue template syntax to render the details of the car. This creates the layout consisting of the header, breadcrumbs, car details, call to actions, and the EditCar component.

We loop over the services array in the car.services object, listing all the services or displaying a message when a car has no services.

In the service loop, we use the moment.js library to format the date.

{{ service.date | moment("DD/MM/YYYY") }}

In the btn-container div, we list our call to actions with their respective functions.

Lastly, we use the Bootstrap modal for the Edit Car form, which is triggered by the Edit button.

In the <script> tag, we import the dependencies and components and declare two queries, namely the getCar and deleteCar queries, which are used to retrieve a single car’s data and delete it.

We then declare our initial component data, props, computed data, components, and methods.

In the beforeMount() lifecycle hook, we run the getCarDetails() function, passing in the id as a variable, which will return all the single car’s data then commit the result to the Vuex store.

The goHome() function will navigate one page back in history.

The deleteCar() function will mutate our data by deleting a car with the id that we passed in as a variable and then commit the result to the Vuex store.


EditCar

EditCar.vue

In the <template> tag, we create a form and bind each form input to its respective value with the v-model directive.

We bind the OK button to the onSubmit event with the following code:

@ok="onSubmit"

In the <script> tag we import the dependencies and declare the editCar query, which is used to edit a single car’s data.

We then declare our initial component data and props.

Finally, we declare the onSubmit(evt) method, which uses the editCar query to update the car, passing in the form data ({ …data })as the new car data, which is then persisted to the Vuex store. The modal is then hidden.


Owner View

By now, you should be picking up a pattern within the code as most of the code in the Owner view has been discussed in previous components and views.

We are reusing the Header component and CarCard component — only this time displaying all the cars belonging to the specific owner.


Conclusion

I really hope this tutorial serves the dual purpose of demonstrating the ease of data retrieval and manipulation using GraphQL and also the ease of coding and intuitive nature of Vue.js.

I know that this tutorial can definitely be even further refactored to reduce the code and complexity. If you have any suggestions, please leave them in the comments.


What's Next?

The only thing left for us to do is deployment. This may sound easy, but we have a couple of loose ends to tie up to truly bring everything together.

So in the next tutorial, we will be moving our codebase to the cloud, including the database, and we will also introduce environmental variables.

As usual, all the code from the above tutorial is available on GitHub.

Better Programming

Advice for programmers.

Siegfried Grimbeek

Written by

Web developer, open source enthusiast and agile evangelist. Currently dreaming big and making things happen @beerwulfwebshop.

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade