Category ROR

Now we move to VueJS part of the project. So, first create the VueJS app with the following command in the front-vue directory:

docker run --rm -v "${PWD}:/$(basename `pwd`)" -w "/$(basename `pwd`)" -it node:14.5-alpine sh -c "yarn global add @vue/cli && vue create . && vue add apollo"

This command will create VueJS app in the directory and add plugin of vue-apollo which we will use for the graphql api backend access.

Then create the front-vue service in docker and start in docker by following commands:

todo-app/docker-compose.yml

	front-vue:
	    build: ./front-vue
	    ports:
	      - "8080:8080" # use port that you want to in your local instead of 8091
	    volumes:
	      - ./front-vue:/front-vue
	      - front_node_modules:/front-vue/node_modules
volumes:
	front_node_modules:

todo-app/front-vue/Dockerfile

FROM node:14.5-alpine
WORKDIR /front-vue
COPY package*.json ./
RUN yarn install
COPY . .
CMD ["yarn", "serve"]
docker-compose up -d --build

Init Project:

We will use following packages which you can see todo-app/front-vue/package.json

Initialise tailwindcss and fontawesome in main.js by adding following code:

todo-app/front-vue/src/main.js

import { library } from "@fortawesome/fontawesome-svg-core";
import {
  faPlus,
  faLongArrowAltLeft,
  faCheckCircle,
  faCircle,
  faTrash,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";

library.add(faPlus, faLongArrowAltLeft, faCheckCircle, faCircle, faTrash);

Vue.component("font-awesome-icon", FontAwesomeIcon);

import "./assets/styles/index.css";

Clear the App.js and other files for code and styling. Also add purging css for production:

todo-app/front-vue/postcss.config.js

const autoprefixer = require("autoprefixer");
const tailwindcss = require("tailwindcss");
const postcssPurgecss = require(`@fullhuman/postcss-purgecss`);

const purgecss = postcssPurgecss({
  // Specify the paths to all of the template files in your project.
  content: ["./public/**/*.html", "./src/**/*.vue"],
  // Include any special characters you're using in this regular expression.
  // See: https://tailwindcss.com/docs/controlling-file-size/#understanding-the-regex
  defaultExtractor: (content) => content.match(/[\w-/:]+(?<!:)/g) || [],
  // Whitelist auto generated classes for transitions and router links.
  // From: https://github.com/ky-is/vue-cli-plugin-tailwind
  whitelistPatterns: [
    /-(leave|enter|appear)(|-(to|from|active))$/,
    /^(?!(|.*?:)cursor-move).+-move$/,
    /^router-link(|-exact)-active$/,
    /svg.*/,
    /fa.*/,
  ],
});

module.exports = {
  plugins: [
    tailwindcss,
    autoprefixer,
    ...(process.env.NODE_ENV === "production" ? [purgecss] : []),
  ],
};

Also add tailwind css file

todo-app/front-vue/src/assets/styles/index.css

@tailwind base;
@tailwind components;
@tailwind utilities;

Change the name of token and url of backend graphql

todo-app/front-vue/src/vue-apollo.js

// Name of the localStorage item
const AUTH_TOKEN = "token";

// Http endpoint
const httpEndpoint =
  process.env.VUE_APP_GRAPHQL_HTTP || "http://localhost:3000/graphql";

GraphQL using Vue-Apollo

There are many ways to make graphql mutation and query through Vue-Apollo which I have explained in different components and views but the main way which I used everywhere, and I like most is making querying and mutation in .gql files separately, and then we can change the query or mutation even after project is created separately in their files. Also, the way the bearer authorisation being handled by the vue-apollo automatically is great.

So, we create a folder graphql in src, and we add all the queries and mutations in gql language there. For example:

todo-app/front-vue/src/graphql/mutations/signUp.gql

mutation signUp($email: String!, $password: String!) {
  signUp(input: { email: $email, password: $password }) {
    token
  }
}

todo-app/front-vue/src/graphql/queries/me.gql

query me {
  me {
    lists {
      id
      name
    }
  }
}

Different ways to make query and mutations

Mutations

We can use mutation as components with template like in signUp:

todo-app/front-vue/src/views/auth/signUp.vue

<ApolloMutation
 :mutation="require('@/graphql/mutations/signUp.gql')"
 :variables="{ email, password }"
 @done="signedUp"
 >
  <template v-slot="{ mutate, loading, error }">
    <!-- Error -->
    <div class="bg-red-100 border-l-4 border-red-400 p-4" v-if="error">
      <div class="flex">
        <div class="flex-shrink-0">
          <svg
            class="h-5 w-5 text-red-400"
            viewBox="0 0 20 20"
            fill="currentColor"
          >
          <path
            fill-rule="evenodd"
            d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
            clip-rule="evenodd"
          />
          </svg>
        </div>
        <div class="ml-3">
          <p class="text-sm leading-5 text-red-700">
            {{ error.toString().replace("Error: GraphQL error: ", "") }}
          </p>
        </div>
      </div>
    </div>
    <!-- Sign Up Form -->
    <form action="#" class="mt-5" @submit.prevent="mutate()">
      <!-- Email -->
      <div class="mb-5">
        <input
          type="email"
          name="email"
          id="email"
          v-model="email"
          class="rounded w-full shadow border border-gray-900 tracking-wide text-xl p-2"
          placeholder="Email"
          autocomplete="on"
          required
        />
      </div>

      <!-- Password -->
      <div class="mb-5">
        <input
          type="password"
          name="password"
          v-model="password"
          class="rounded w-full shadow border border-gray-900 tracking-wide text-xl p-2"
          placeholder="Password"
          id="sign-up-password"
          autocomplete="off"
        />
      </div>

      <div class="text-center flex justify-between">
        <!-- sign in link -->
        <router-link
          to="/sign_in"
          class="self-center hover:underline hover:text-gray-800"
          >Sign In</router-link
        >
        <!-- sign up link -->
        <button
          type="submit"
          :disabled="loading"
          class="px-4 py-2 rounded tracking-wide font-semibold bg-black text-white text-lg hover:bg-gray-800 shadow-lg"
        >
          Sign Up
        </button>
      </div>
    </form>
  </template>
</ApolloMutation>

Second way is to use in normal vue method like below:

todo-app/front-vue/src/views/listShow.vue

async changeStatus(task) {
  const result = await this.$apollo.mutate({
    mutation: require("@/graphql/mutations/changeTaskStatus.gql"),
    variables: {
      id: task.id,
    },
  });
  if (!!result.data) {
    await this.loadList();
  }
},

Queries

First way is graphql query as component as we have seen in mutations.

Second way is the same to call in function same as that of mutations like:

todo-app/front-vue/src/views/listShow.vue

async getList() {
  return await this.$apollo.query({
    query: require("@/graphql/queries/showList.gql"),
    variables: {
      id: this.$route.params.listId,
    },
    fetchPolicy: "network-only",
  });
},

A third way is to call query in Apollo object in the js section of the vue component like:

todo-app/front-vue/src/views/Home.vue

apollo: {
  me: {
    query: require("@/graphql/queries/me.gql"),
    result({ data }) {
      if (data) {
        this.lists = data.me.lists;
        this.loading = false;
      }
    },
  },
},

This article is written by Senior Solutions Architect, Sulman Baig from UNATION. With over 8 years of professional experience, Sulman is a Ruby on Rails developer with 5+ years of experience in Ruby, alongside NodeJS and VueJS.

Previously he worked at MailMunch as Principal Software Engineer, and as a CTO at GoGhoom.

Sulman has worked in agile environments with complex industrial-grade applications with technologies like Swagger REST API, MVC architectures and database architecture designs.